android_net_TrafficStats.cpp revision c39c1d4dee917560d174f6ba5402e4c6644edd47
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 133// Total stats are read less often, so we're willing to put up 134// with listing the directory and concatenating filenames. 135 136static jlong getTotalTxPackets(JNIEnv* env, jobject clazz) { 137 return readTotal("/statistics/tx_packets"); 138} 139 140static jlong getTotalRxPackets(JNIEnv* env, jobject clazz) { 141 return readTotal("/statistics/rx_packets"); 142} 143 144static jlong getTotalTxBytes(JNIEnv* env, jobject clazz) { 145 return readTotal("/statistics/tx_bytes"); 146} 147 148static jlong getTotalRxBytes(JNIEnv* env, jobject clazz) { 149 return readTotal("/statistics/rx_bytes"); 150} 151 152// Per-UID stats require reading from a constructed filename. 153 154static jlong getUidBytes(JNIEnv* env, jobject clazz, jint uid, 155 enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { 156 char tcp_filename[80], udp_filename[80]; 157 jlong tcp_bytes = -1, udp_bytes = -1, total_bytes = -1; 158 159 switch (tx_or_rx) { 160 case TX: 161 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd", uid); 162 sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd", uid); 163 break; 164 case RX: 165 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv", uid); 166 sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv", uid); 167 break; 168 default: 169 return -1; 170 } 171 172 switch (tcp_or_udp) { 173 case TCP: 174 tcp_bytes = readNumber(tcp_filename); 175 total_bytes = (tcp_bytes >= 0) ? tcp_bytes : -1; 176 break; 177 case UDP: 178 udp_bytes = readNumber(udp_filename); 179 total_bytes = (udp_bytes >= 0) ? udp_bytes : -1; 180 break; 181 case TCP_AND_UDP: 182 tcp_bytes = readNumber(tcp_filename); 183 total_bytes += (tcp_bytes >= 0 ? tcp_bytes : 0); 184 185 udp_bytes = readNumber(udp_filename); 186 total_bytes += (udp_bytes >= 0 ? udp_bytes : 0); 187 break; 188 default: 189 return -1; 190 } 191 192 return total_bytes; 193} 194 195static jlong getUidPkts(JNIEnv* env, jobject clazz, jint uid, 196 enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { 197 char tcp_filename[80], udp_filename[80]; 198 jlong tcp_pkts = -1, udp_pkts = -1, total_pkts = -1; 199 200 switch (tx_or_rx) { 201 case TX: 202 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd_pkt", uid); 203 sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd_pkt", uid); 204 break; 205 case RX: 206 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv_pkt", uid); 207 sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv_pkt", uid); 208 break; 209 default: 210 return -1; 211 } 212 213 switch (tcp_or_udp) { 214 case TCP: 215 tcp_pkts = readNumber(tcp_filename); 216 total_pkts = (tcp_pkts >= 0) ? tcp_pkts : -1; 217 break; 218 case UDP: 219 udp_pkts = readNumber(udp_filename); 220 total_pkts = (udp_pkts >= 0) ? udp_pkts : -1; 221 break; 222 case TCP_AND_UDP: 223 tcp_pkts = readNumber(tcp_filename); 224 total_pkts += (tcp_pkts >= 0 ? tcp_pkts : 0); 225 226 udp_pkts = readNumber(udp_filename); 227 total_pkts += (udp_pkts >= 0 ? udp_pkts : 0); 228 break; 229 default: 230 return -1; 231 } 232 233 return total_pkts; 234} 235 236static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) { 237 return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP); 238} 239 240static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) { 241 return getUidBytes(env, clazz, uid, TX, TCP_AND_UDP); 242} 243 244/* TCP Segments + UDP Packets */ 245static jlong getUidTxPackets(JNIEnv* env, jobject clazz, jint uid) { 246 return getUidPkts(env, clazz, uid, TX, TCP_AND_UDP); 247} 248 249/* TCP Segments + UDP Packets */ 250static jlong getUidRxPackets(JNIEnv* env, jobject clazz, jint uid) { 251 return getUidPkts(env, clazz, uid, RX, TCP_AND_UDP); 252} 253 254static jlong getUidTcpTxBytes(JNIEnv* env, jobject clazz, jint uid) { 255 return getUidBytes(env, clazz, uid, TX, TCP); 256} 257 258static jlong getUidTcpRxBytes(JNIEnv* env, jobject clazz, jint uid) { 259 return getUidBytes(env, clazz, uid, RX, TCP); 260} 261 262static jlong getUidUdpTxBytes(JNIEnv* env, jobject clazz, jint uid) { 263 return getUidBytes(env, clazz, uid, TX, UDP); 264} 265 266static jlong getUidUdpRxBytes(JNIEnv* env, jobject clazz, jint uid) { 267 return getUidBytes(env, clazz, uid, RX, UDP); 268} 269 270static jlong getUidTcpTxSegments(JNIEnv* env, jobject clazz, jint uid) { 271 return getUidPkts(env, clazz, uid, TX, TCP); 272} 273 274static jlong getUidTcpRxSegments(JNIEnv* env, jobject clazz, jint uid) { 275 return getUidPkts(env, clazz, uid, RX, TCP); 276} 277 278static jlong getUidUdpTxPackets(JNIEnv* env, jobject clazz, jint uid) { 279 return getUidPkts(env, clazz, uid, TX, UDP); 280} 281 282static jlong getUidUdpRxPackets(JNIEnv* env, jobject clazz, jint uid) { 283 return getUidPkts(env, clazz, uid, RX, UDP); 284} 285 286static JNINativeMethod gMethods[] = { 287 {"getMobileTxPackets", "()J", (void*) getMobileTxPackets}, 288 {"getMobileRxPackets", "()J", (void*) getMobileRxPackets}, 289 {"getMobileTxBytes", "()J", (void*) getMobileTxBytes}, 290 {"getMobileRxBytes", "()J", (void*) getMobileRxBytes}, 291 {"getTotalTxPackets", "()J", (void*) getTotalTxPackets}, 292 {"getTotalRxPackets", "()J", (void*) getTotalRxPackets}, 293 {"getTotalTxBytes", "()J", (void*) getTotalTxBytes}, 294 {"getTotalRxBytes", "()J", (void*) getTotalRxBytes}, 295 296 /* Per-UID Stats */ 297 {"getUidTxBytes", "(I)J", (void*) getUidTxBytes}, 298 {"getUidRxBytes", "(I)J", (void*) getUidRxBytes}, 299 {"getUidTxPackets", "(I)J", (void*) getUidTxPackets}, 300 {"getUidRxPackets", "(I)J", (void*) getUidRxPackets}, 301 302 {"getUidTcpTxBytes", "(I)J", (void*) getUidTcpTxBytes}, 303 {"getUidTcpRxBytes", "(I)J", (void*) getUidTcpRxBytes}, 304 {"getUidUdpTxBytes", "(I)J", (void*) getUidUdpTxBytes}, 305 {"getUidUdpRxBytes", "(I)J", (void*) getUidUdpRxBytes}, 306 307 {"getUidTcpTxSegments", "(I)J", (void*) getUidTcpTxSegments}, 308 {"getUidTcpRxSegments", "(I)J", (void*) getUidTcpRxSegments}, 309 {"getUidUdpTxPackets", "(I)J", (void*) getUidUdpTxPackets}, 310 {"getUidUdpRxPackets", "(I)J", (void*) getUidUdpRxPackets}, 311}; 312 313int register_android_net_TrafficStats(JNIEnv* env) { 314 return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats", 315 gMethods, NELEM(gMethods)); 316} 317 318} 319