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