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