android_net_TrafficStats.cpp revision 4e8d8238e10d168b434dcdd2e779a9aab37f1e2c
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 33// Returns an ASCII decimal number read from the specified file, -1 on error. 34static jlong readNumber(char const* filename) { 35#ifdef HAVE_ANDROID_OS 36 char buf[80]; 37 int fd = open(filename, O_RDONLY); 38 if (fd < 0) { 39 if (errno != ENOENT) LOGE("Can't open %s: %s", filename, strerror(errno)); 40 return -1; 41 } 42 43 int len = read(fd, buf, sizeof(buf) - 1); 44 if (len < 0) { 45 LOGE("Can't read %s: %s", filename, strerror(errno)); 46 close(fd); 47 return -1; 48 } 49 50 close(fd); 51 buf[len] = '\0'; 52 return atoll(buf); 53#else // Simulator 54 return -1; 55#endif 56} 57 58// Return the number from the first file which exists and contains data 59static jlong tryBoth(char const* a, char const* b) { 60 jlong num = readNumber(a); 61 return num >= 0 ? num : readNumber(b); 62} 63 64// Returns the sum of numbers from the specified path under /sys/class/net/*, 65// -1 if no such file exists. 66static jlong readTotal(char const* suffix) { 67#ifdef HAVE_ANDROID_OS 68 char filename[PATH_MAX] = "/sys/class/net/"; 69 DIR *dir = opendir(filename); 70 if (dir == NULL) { 71 LOGE("Can't list %s: %s", filename, strerror(errno)); 72 return -1; 73 } 74 75 int len = strlen(filename); 76 jlong total = -1; 77 while (struct dirent *entry = readdir(dir)) { 78 // Skip ., .., and localhost interfaces. 79 if (entry->d_name[0] != '.' && strncmp(entry->d_name, "lo", 2) != 0) { 80 strlcpy(filename + len, entry->d_name, sizeof(filename) - len); 81 strlcat(filename, suffix, sizeof(filename)); 82 jlong num = readNumber(filename); 83 if (num >= 0) total = total < 0 ? num : total + num; 84 } 85 } 86 87 closedir(dir); 88 return total; 89#else // Simulator 90 return -1; 91#endif 92} 93 94// Mobile stats get accessed a lot more often than total stats. 95// Note the individual files can come and go at runtime, so we check 96// each file every time (rather than caching which ones exist). 97 98static jlong getMobileTxPackets(JNIEnv* env, jobject clazz) { 99 return tryBoth( 100 "/sys/class/net/rmnet0/statistics/tx_packets", 101 "/sys/class/net/ppp0/statistics/tx_packets"); 102} 103 104static jlong getMobileRxPackets(JNIEnv* env, jobject clazz) { 105 return tryBoth( 106 "/sys/class/net/rmnet0/statistics/rx_packets", 107 "/sys/class/net/ppp0/statistics/rx_packets"); 108} 109 110static jlong getMobileTxBytes(JNIEnv* env, jobject clazz) { 111 return tryBoth( 112 "/sys/class/net/rmnet0/statistics/tx_bytes", 113 "/sys/class/net/ppp0/statistics/tx_bytes"); 114} 115 116static jlong getMobileRxBytes(JNIEnv* env, jobject clazz) { 117 return tryBoth( 118 "/sys/class/net/rmnet0/statistics/rx_bytes", 119 "/sys/class/net/ppp0/statistics/rx_bytes"); 120} 121 122// Total stats are read less often, so we're willing to put up 123// with listing the directory and concatenating filenames. 124 125static jlong getTotalTxPackets(JNIEnv* env, jobject clazz) { 126 return readTotal("/statistics/tx_packets"); 127} 128 129static jlong getTotalRxPackets(JNIEnv* env, jobject clazz) { 130 return readTotal("/statistics/rx_packets"); 131} 132 133static jlong getTotalTxBytes(JNIEnv* env, jobject clazz) { 134 return readTotal("/statistics/tx_bytes"); 135} 136 137static jlong getTotalRxBytes(JNIEnv* env, jobject clazz) { 138 return readTotal("/statistics/rx_bytes"); 139} 140 141// Per-UID stats require reading from a constructed filename. 142 143static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) { 144 char filename[80]; 145 sprintf(filename, "/proc/uid_stat/%d/tcp_rcv", uid); 146 return readNumber(filename); 147} 148 149static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) { 150 char filename[80]; 151 sprintf(filename, "/proc/uid_stat/%d/tcp_snd", uid); 152 return readNumber(filename); 153} 154 155static JNINativeMethod gMethods[] = { 156 {"getMobileTxPackets", "()J", (void*) getMobileTxPackets}, 157 {"getMobileRxPackets", "()J", (void*) getMobileRxPackets}, 158 {"getMobileTxBytes", "()J", (void*) getMobileTxBytes}, 159 {"getMobileRxBytes", "()J", (void*) getMobileRxBytes}, 160 {"getTotalTxPackets", "()J", (void*) getTotalTxPackets}, 161 {"getTotalRxPackets", "()J", (void*) getTotalRxPackets}, 162 {"getTotalTxBytes", "()J", (void*) getTotalTxBytes}, 163 {"getTotalRxBytes", "()J", (void*) getTotalRxBytes}, 164 {"getUidTxBytes", "(I)J", (void*) getUidTxBytes}, 165 {"getUidRxBytes", "(I)J", (void*) getUidRxBytes}, 166}; 167 168int register_android_net_TrafficStats(JNIEnv* env) { 169 return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats", 170 gMethods, NELEM(gMethods)); 171} 172 173} 174