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 34static const uint64_t VALUE_UNKNOWN = -1; 35static const char* IFACE_STAT_ALL = "/proc/net/xt_qtaguid/iface_stat_all"; 36 37enum Tx_Rx { 38 TX, 39 RX 40}; 41 42enum Tcp_Udp { 43 TCP, 44 UDP, 45 TCP_AND_UDP 46}; 47 48// NOTE: keep these in sync with TrafficStats.java 49enum IfaceStatType { 50 RX_BYTES = 0, 51 RX_PACKETS = 1, 52 TX_BYTES = 2, 53 TX_PACKETS = 3 54}; 55 56struct IfaceStat { 57 uint64_t rxBytes; 58 uint64_t rxPackets; 59 uint64_t txBytes; 60 uint64_t txPackets; 61}; 62 63// Returns an ASCII decimal number read from the specified file, -1 on error. 64static jlong readNumber(char const* filename) { 65 char buf[80]; 66 int fd = open(filename, O_RDONLY); 67 if (fd < 0) { 68 if (errno != ENOENT) ALOGE("Can't open %s: %s", filename, strerror(errno)); 69 return -1; 70 } 71 72 int len = read(fd, buf, sizeof(buf) - 1); 73 if (len < 0) { 74 ALOGE("Can't read %s: %s", filename, strerror(errno)); 75 close(fd); 76 return -1; 77 } 78 79 close(fd); 80 buf[len] = '\0'; 81 return atoll(buf); 82} 83 84static int parseIfaceStat(const char* iface, struct IfaceStat* stat) { 85 FILE *fp = fopen(IFACE_STAT_ALL, "r"); 86 if (!fp) { 87 return errno; 88 } 89 90 char buffer[256]; 91 char cur_iface[32]; 92 int active; 93 uint64_t rxBytes, rxPackets, txBytes, txPackets, devRxBytes, devRxPackets, devTxBytes, 94 devTxPackets; 95 96 while (fgets(buffer, 256, fp) != NULL) { 97 if (sscanf(buffer, "%31s %d %llu %llu %llu %llu %llu %llu %llu %llu", cur_iface, &active, 98 &rxBytes, &rxPackets, &txBytes, &txPackets, &devRxBytes, &devRxPackets, 99 &devTxBytes, &devTxPackets) != 10) { 100 continue; 101 } 102 103 if (!iface || !strcmp(iface, cur_iface)) { 104 stat->rxBytes += rxBytes; 105 stat->rxPackets += rxPackets; 106 stat->txBytes += txBytes; 107 stat->txPackets += txPackets; 108 109 if (active) { 110 stat->rxBytes += devRxBytes; 111 stat->rxPackets += devRxPackets; 112 stat->txBytes += devTxBytes; 113 stat->txPackets += devTxPackets; 114 } 115 } 116 } 117 118 fclose(fp); 119 return 0; 120} 121 122static uint64_t getIfaceStatType(const char* iface, IfaceStatType type) { 123 struct IfaceStat stat; 124 memset(&stat, 0, sizeof(IfaceStat)); 125 126 if (parseIfaceStat(iface, &stat)) { 127 return VALUE_UNKNOWN; 128 } 129 130 switch (type) { 131 case RX_BYTES: 132 return stat.rxBytes; 133 case RX_PACKETS: 134 return stat.rxPackets; 135 case TX_BYTES: 136 return stat.txBytes; 137 case TX_PACKETS: 138 return stat.txPackets; 139 default: 140 return VALUE_UNKNOWN; 141 } 142} 143 144static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) { 145 return getIfaceStatType(NULL, (IfaceStatType) type); 146} 147 148static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) { 149 struct IfaceStat stat; 150 const char* ifaceChars = env->GetStringUTFChars(iface, NULL); 151 if (ifaceChars) { 152 uint64_t stat = getIfaceStatType(ifaceChars, (IfaceStatType) type); 153 env->ReleaseStringUTFChars(iface, ifaceChars); 154 return stat; 155 } else { 156 return VALUE_UNKNOWN; 157 } 158} 159 160 161// Per-UID stats require reading from a constructed filename. 162 163static jlong getUidBytes(JNIEnv* env, jobject clazz, jint uid, 164 enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { 165 char tcp_filename[80], udp_filename[80]; 166 jlong tcp_bytes = -1, udp_bytes = -1, total_bytes = -1; 167 168 switch (tx_or_rx) { 169 case TX: 170 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd", uid); 171 sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd", uid); 172 break; 173 case RX: 174 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv", uid); 175 sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv", uid); 176 break; 177 default: 178 return -1; 179 } 180 181 switch (tcp_or_udp) { 182 case TCP: 183 tcp_bytes = readNumber(tcp_filename); 184 total_bytes = (tcp_bytes >= 0) ? tcp_bytes : -1; 185 break; 186 case UDP: 187 udp_bytes = readNumber(udp_filename); 188 total_bytes = (udp_bytes >= 0) ? udp_bytes : -1; 189 break; 190 case TCP_AND_UDP: 191 tcp_bytes = readNumber(tcp_filename); 192 total_bytes += (tcp_bytes >= 0 ? tcp_bytes : 0); 193 194 udp_bytes = readNumber(udp_filename); 195 total_bytes += (udp_bytes >= 0 ? udp_bytes : 0); 196 break; 197 default: 198 return -1; 199 } 200 201 return total_bytes; 202} 203 204static jlong getUidPkts(JNIEnv* env, jobject clazz, jint uid, 205 enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { 206 char tcp_filename[80], udp_filename[80]; 207 jlong tcp_pkts = -1, udp_pkts = -1, total_pkts = -1; 208 209 switch (tx_or_rx) { 210 case TX: 211 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd_pkt", uid); 212 sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd_pkt", uid); 213 break; 214 case RX: 215 sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv_pkt", uid); 216 sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv_pkt", uid); 217 break; 218 default: 219 return -1; 220 } 221 222 switch (tcp_or_udp) { 223 case TCP: 224 tcp_pkts = readNumber(tcp_filename); 225 total_pkts = (tcp_pkts >= 0) ? tcp_pkts : -1; 226 break; 227 case UDP: 228 udp_pkts = readNumber(udp_filename); 229 total_pkts = (udp_pkts >= 0) ? udp_pkts : -1; 230 break; 231 case TCP_AND_UDP: 232 tcp_pkts = readNumber(tcp_filename); 233 total_pkts += (tcp_pkts >= 0 ? tcp_pkts : 0); 234 235 udp_pkts = readNumber(udp_filename); 236 total_pkts += (udp_pkts >= 0 ? udp_pkts : 0); 237 break; 238 default: 239 return -1; 240 } 241 242 return total_pkts; 243} 244 245static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) { 246 return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP); 247} 248 249static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) { 250 return getUidBytes(env, clazz, uid, TX, TCP_AND_UDP); 251} 252 253/* TCP Segments + UDP Packets */ 254static jlong getUidTxPackets(JNIEnv* env, jobject clazz, jint uid) { 255 return getUidPkts(env, clazz, uid, TX, TCP_AND_UDP); 256} 257 258/* TCP Segments + UDP Packets */ 259static jlong getUidRxPackets(JNIEnv* env, jobject clazz, jint uid) { 260 return getUidPkts(env, clazz, uid, RX, TCP_AND_UDP); 261} 262 263static jlong getUidTcpTxBytes(JNIEnv* env, jobject clazz, jint uid) { 264 return getUidBytes(env, clazz, uid, TX, TCP); 265} 266 267static jlong getUidTcpRxBytes(JNIEnv* env, jobject clazz, jint uid) { 268 return getUidBytes(env, clazz, uid, RX, TCP); 269} 270 271static jlong getUidUdpTxBytes(JNIEnv* env, jobject clazz, jint uid) { 272 return getUidBytes(env, clazz, uid, TX, UDP); 273} 274 275static jlong getUidUdpRxBytes(JNIEnv* env, jobject clazz, jint uid) { 276 return getUidBytes(env, clazz, uid, RX, UDP); 277} 278 279static jlong getUidTcpTxSegments(JNIEnv* env, jobject clazz, jint uid) { 280 return getUidPkts(env, clazz, uid, TX, TCP); 281} 282 283static jlong getUidTcpRxSegments(JNIEnv* env, jobject clazz, jint uid) { 284 return getUidPkts(env, clazz, uid, RX, TCP); 285} 286 287static jlong getUidUdpTxPackets(JNIEnv* env, jobject clazz, jint uid) { 288 return getUidPkts(env, clazz, uid, TX, UDP); 289} 290 291static jlong getUidUdpRxPackets(JNIEnv* env, jobject clazz, jint uid) { 292 return getUidPkts(env, clazz, uid, RX, UDP); 293} 294 295static JNINativeMethod gMethods[] = { 296 {"nativeGetTotalStat", "(I)J", (void*) getTotalStat}, 297 {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat}, 298 299 /* Per-UID Stats */ 300 {"getUidTxBytes", "(I)J", (void*) getUidTxBytes}, 301 {"getUidRxBytes", "(I)J", (void*) getUidRxBytes}, 302 {"getUidTxPackets", "(I)J", (void*) getUidTxPackets}, 303 {"getUidRxPackets", "(I)J", (void*) getUidRxPackets}, 304 305 {"getUidTcpTxBytes", "(I)J", (void*) getUidTcpTxBytes}, 306 {"getUidTcpRxBytes", "(I)J", (void*) getUidTcpRxBytes}, 307 {"getUidUdpTxBytes", "(I)J", (void*) getUidUdpTxBytes}, 308 {"getUidUdpRxBytes", "(I)J", (void*) getUidUdpRxBytes}, 309 310 {"getUidTcpTxSegments", "(I)J", (void*) getUidTcpTxSegments}, 311 {"getUidTcpRxSegments", "(I)J", (void*) getUidTcpRxSegments}, 312 {"getUidUdpTxPackets", "(I)J", (void*) getUidUdpTxPackets}, 313 {"getUidUdpRxPackets", "(I)J", (void*) getUidUdpRxPackets}, 314}; 315 316int register_android_net_TrafficStats(JNIEnv* env) { 317 return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats", 318 gMethods, NELEM(gMethods)); 319} 320 321} 322