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