android_net_TrafficStats.cpp revision c1b9bbb21c8ad5109978a4e9e770cd18b0257434
1/* 2 * Copyright (C) 2010 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#define LOG_TAG "TrafficStats" 18 19#include <dirent.h> 20#include <errno.h> 21#include <fcntl.h> 22#include <sys/stat.h> 23#include <sys/types.h> 24 25#include <android_runtime/AndroidRuntime.h> 26#include <cutils/logger.h> 27#include <jni.h> 28#include <ScopedUtfChars.h> 29#include <utils/misc.h> 30#include <utils/Log.h> 31 32namespace android { 33 34enum Tx_Rx { 35 TX, 36 RX 37}; 38 39enum Tcp_Udp { 40 TCP, 41 UDP, 42 TCP_AND_UDP 43}; 44 45// Returns an ASCII decimal number read from the specified file, -1 on error. 46static jlong readNumber(char const* filename) { 47 char buf[80]; 48 int fd = open(filename, O_RDONLY); 49 if (fd < 0) { 50 if (errno != ENOENT) LOGE("Can't open %s: %s", filename, strerror(errno)); 51 return -1; 52 } 53 54 int len = read(fd, buf, sizeof(buf) - 1); 55 if (len < 0) { 56 LOGE("Can't read %s: %s", filename, strerror(errno)); 57 close(fd); 58 return -1; 59 } 60 61 close(fd); 62 buf[len] = '\0'; 63 return atoll(buf); 64} 65 66static const char* mobile_iface_list[] = { 67 "rmnet0", 68 "rmnet1", 69 "rmnet2", 70 "rmnet3", 71 "ppp0", 72 0 73}; 74 75static jlong getAll(const char** iface_list, const char* what) { 76 77 char filename[80]; 78 int idx = 0; 79 bool supported = false; 80 jlong total = 0; 81 while (iface_list[idx] != 0) { 82 83 snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", 84 iface_list[idx], what); 85 jlong number = readNumber(filename); 86 if (number >= 0) { 87 supported = true; 88 total += number; 89 } 90 idx++; 91 } 92 if (supported) return total; 93 94 return -1; 95} 96 97// Returns the sum of numbers from the specified path under /sys/class/net/*, 98// -1 if no such file exists. 99static jlong readTotal(char const* suffix) { 100 char filename[PATH_MAX] = "/sys/class/net/"; 101 DIR *dir = opendir(filename); 102 if (dir == NULL) { 103 LOGE("Can't list %s: %s", filename, strerror(errno)); 104 return -1; 105 } 106 107 int len = strlen(filename); 108 jlong total = -1; 109 while (struct dirent *entry = readdir(dir)) { 110 // Skip ., .., and localhost interfaces. 111 if (entry->d_name[0] != '.' && strncmp(entry->d_name, "lo", 2) != 0) { 112 strlcpy(filename + len, entry->d_name, sizeof(filename) - len); 113 strlcat(filename, suffix, sizeof(filename)); 114 jlong num = readNumber(filename); 115 if (num >= 0) total = total < 0 ? num : total + num; 116 } 117 } 118 119 closedir(dir); 120 return total; 121} 122 123// Mobile stats get accessed a lot more often than total stats. 124// Note the individual files can come and go at runtime, so we check 125// each file every time (rather than caching which ones exist). 126 127static jlong getMobileTxPackets(JNIEnv* env, jobject clazz) { 128 return getAll(mobile_iface_list, "tx_packets"); 129} 130 131static jlong getMobileRxPackets(JNIEnv* env, jobject clazz) { 132 return getAll(mobile_iface_list, "rx_packets"); 133} 134 135static jlong getMobileTxBytes(JNIEnv* env, jobject clazz) { 136 return getAll(mobile_iface_list, "tx_bytes"); 137} 138 139static jlong getMobileRxBytes(JNIEnv* env, jobject clazz) { 140 return getAll(mobile_iface_list, "rx_bytes"); 141} 142 143static jlong getData(JNIEnv* env, const char* what, jstring javaInterface) { 144 ScopedUtfChars interface(env, javaInterface); 145 if (interface.c_str() == NULL) { 146 return -1; 147 } 148 149 char filename[80]; 150 snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interface.c_str(), what); 151 return readNumber(filename); 152} 153 154static jlong getTxPackets(JNIEnv* env, jobject clazz, jstring interface) { 155 return getData(env, "tx_packets", interface); 156} 157 158static jlong getRxPackets(JNIEnv* env, jobject clazz, jstring interface) { 159 return getData(env, "rx_packets", interface); 160} 161 162static jlong getTxBytes(JNIEnv* env, jobject clazz, jstring interface) { 163 return getData(env, "tx_bytes", interface); 164} 165 166static jlong getRxBytes(JNIEnv* env, jobject clazz, jstring interface) { 167 return getData(env, "rx_bytes", interface); 168} 169 170 171// Total stats are read less often, so we're willing to put up 172// with listing the directory and concatenating filenames. 173 174static jlong getTotalTxPackets(JNIEnv* env, jobject clazz) { 175 return readTotal("/statistics/tx_packets"); 176} 177 178static jlong getTotalRxPackets(JNIEnv* env, jobject clazz) { 179 return readTotal("/statistics/rx_packets"); 180} 181 182static jlong getTotalTxBytes(JNIEnv* env, jobject clazz) { 183 return readTotal("/statistics/tx_bytes"); 184} 185 186static jlong getTotalRxBytes(JNIEnv* env, jobject clazz) { 187 return readTotal("/statistics/rx_bytes"); 188} 189 190// Per-UID stats require reading from a constructed filename. 191 192static jlong getUidBytes(JNIEnv* env, jobject clazz, jint uid, 193 enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { 194 char tcp_filename[80], udp_filename[80]; 195 jlong tcp_bytes = -1, udp_bytes = -1, total_bytes = -1; 196 197 switch (tx_or_rx) { 198 case TX: 199 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd", uid); 200 sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd", uid); 201 break; 202 case RX: 203 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv", uid); 204 sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv", uid); 205 break; 206 default: 207 return -1; 208 } 209 210 switch (tcp_or_udp) { 211 case TCP: 212 tcp_bytes = readNumber(tcp_filename); 213 total_bytes = (tcp_bytes >= 0) ? tcp_bytes : -1; 214 break; 215 case UDP: 216 udp_bytes = readNumber(udp_filename); 217 total_bytes = (udp_bytes >= 0) ? udp_bytes : -1; 218 break; 219 case TCP_AND_UDP: 220 tcp_bytes = readNumber(tcp_filename); 221 total_bytes += (tcp_bytes >= 0 ? tcp_bytes : 0); 222 223 udp_bytes = readNumber(udp_filename); 224 total_bytes += (udp_bytes >= 0 ? udp_bytes : 0); 225 break; 226 default: 227 return -1; 228 } 229 230 return total_bytes; 231} 232 233static jlong getUidPkts(JNIEnv* env, jobject clazz, jint uid, 234 enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { 235 char tcp_filename[80], udp_filename[80]; 236 jlong tcp_pkts = -1, udp_pkts = -1, total_pkts = -1; 237 238 switch (tx_or_rx) { 239 case TX: 240 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd_pkt", uid); 241 sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd_pkt", uid); 242 break; 243 case RX: 244 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv_pkt", uid); 245 sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv_pkt", uid); 246 break; 247 default: 248 return -1; 249 } 250 251 switch (tcp_or_udp) { 252 case TCP: 253 tcp_pkts = readNumber(tcp_filename); 254 total_pkts = (tcp_pkts >= 0) ? tcp_pkts : -1; 255 break; 256 case UDP: 257 udp_pkts = readNumber(udp_filename); 258 total_pkts = (udp_pkts >= 0) ? udp_pkts : -1; 259 break; 260 case TCP_AND_UDP: 261 tcp_pkts = readNumber(tcp_filename); 262 total_pkts += (tcp_pkts >= 0 ? tcp_pkts : 0); 263 264 udp_pkts = readNumber(udp_filename); 265 total_pkts += (udp_pkts >= 0 ? udp_pkts : 0); 266 break; 267 default: 268 return -1; 269 } 270 271 return total_pkts; 272} 273 274static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) { 275 return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP); 276} 277 278static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) { 279 return getUidBytes(env, clazz, uid, TX, TCP_AND_UDP); 280} 281 282/* TCP Segments + UDP Packets */ 283static jlong getUidTxPackets(JNIEnv* env, jobject clazz, jint uid) { 284 return getUidPkts(env, clazz, uid, TX, TCP_AND_UDP); 285} 286 287/* TCP Segments + UDP Packets */ 288static jlong getUidRxPackets(JNIEnv* env, jobject clazz, jint uid) { 289 return getUidPkts(env, clazz, uid, RX, TCP_AND_UDP); 290} 291 292static jlong getUidTcpTxBytes(JNIEnv* env, jobject clazz, jint uid) { 293 return getUidBytes(env, clazz, uid, TX, TCP); 294} 295 296static jlong getUidTcpRxBytes(JNIEnv* env, jobject clazz, jint uid) { 297 return getUidBytes(env, clazz, uid, RX, TCP); 298} 299 300static jlong getUidUdpTxBytes(JNIEnv* env, jobject clazz, jint uid) { 301 return getUidBytes(env, clazz, uid, TX, UDP); 302} 303 304static jlong getUidUdpRxBytes(JNIEnv* env, jobject clazz, jint uid) { 305 return getUidBytes(env, clazz, uid, RX, UDP); 306} 307 308static jlong getUidTcpTxSegments(JNIEnv* env, jobject clazz, jint uid) { 309 return getUidPkts(env, clazz, uid, TX, TCP); 310} 311 312static jlong getUidTcpRxSegments(JNIEnv* env, jobject clazz, jint uid) { 313 return getUidPkts(env, clazz, uid, RX, TCP); 314} 315 316static jlong getUidUdpTxPackets(JNIEnv* env, jobject clazz, jint uid) { 317 return getUidPkts(env, clazz, uid, TX, UDP); 318} 319 320static jlong getUidUdpRxPackets(JNIEnv* env, jobject clazz, jint uid) { 321 return getUidPkts(env, clazz, uid, RX, UDP); 322} 323 324static JNINativeMethod gMethods[] = { 325 {"getMobileTxPackets", "()J", (void*) getMobileTxPackets}, 326 {"getMobileRxPackets", "()J", (void*) getMobileRxPackets}, 327 {"getMobileTxBytes", "()J", (void*) getMobileTxBytes}, 328 {"getMobileRxBytes", "()J", (void*) getMobileRxBytes}, 329 {"getTxPackets", "(Ljava/lang/String;)J", (void*) getTxPackets}, 330 {"getRxPackets", "(Ljava/lang/String;)J", (void*) getRxPackets}, 331 {"getTxBytes", "(Ljava/lang/String;)J", (void*) getTxBytes}, 332 {"getRxBytes", "(Ljava/lang/String;)J", (void*) getRxBytes}, 333 {"getTotalTxPackets", "()J", (void*) getTotalTxPackets}, 334 {"getTotalRxPackets", "()J", (void*) getTotalRxPackets}, 335 {"getTotalTxBytes", "()J", (void*) getTotalTxBytes}, 336 {"getTotalRxBytes", "()J", (void*) getTotalRxBytes}, 337 338 /* Per-UID Stats */ 339 {"getUidTxBytes", "(I)J", (void*) getUidTxBytes}, 340 {"getUidRxBytes", "(I)J", (void*) getUidRxBytes}, 341 {"getUidTxPackets", "(I)J", (void*) getUidTxPackets}, 342 {"getUidRxPackets", "(I)J", (void*) getUidRxPackets}, 343 344 {"getUidTcpTxBytes", "(I)J", (void*) getUidTcpTxBytes}, 345 {"getUidTcpRxBytes", "(I)J", (void*) getUidTcpRxBytes}, 346 {"getUidUdpTxBytes", "(I)J", (void*) getUidUdpTxBytes}, 347 {"getUidUdpRxBytes", "(I)J", (void*) getUidUdpRxBytes}, 348 349 {"getUidTcpTxSegments", "(I)J", (void*) getUidTcpTxSegments}, 350 {"getUidTcpRxSegments", "(I)J", (void*) getUidTcpRxSegments}, 351 {"getUidUdpTxPackets", "(I)J", (void*) getUidUdpTxPackets}, 352 {"getUidUdpRxPackets", "(I)J", (void*) getUidUdpRxPackets}, 353}; 354 355int register_android_net_TrafficStats(JNIEnv* env) { 356 return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats", 357 gMethods, NELEM(gMethods)); 358} 359 360} 361