Home | History | Annotate | Download | only in jni
      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 
     32 namespace android {
     33 
     34 static const uint64_t VALUE_UNKNOWN = -1;
     35 static const char* IFACE_STAT_ALL = "/proc/net/xt_qtaguid/iface_stat_all";
     36 
     37 enum Tx_Rx {
     38     TX,
     39     RX
     40 };
     41 
     42 enum Tcp_Udp {
     43     TCP,
     44     UDP,
     45     TCP_AND_UDP
     46 };
     47 
     48 // NOTE: keep these in sync with TrafficStats.java
     49 enum IfaceStatType {
     50     RX_BYTES = 0,
     51     RX_PACKETS = 1,
     52     TX_BYTES = 2,
     53     TX_PACKETS = 3
     54 };
     55 
     56 struct 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.
     64 static 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 
     84 static 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 
    122 static 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 
    144 static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
    145     return getIfaceStatType(NULL, (IfaceStatType) type);
    146 }
    147 
    148 static 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 
    163 static 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 
    204 static 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 
    245 static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) {
    246     return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP);
    247 }
    248 
    249 static 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 */
    254 static 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 */
    259 static jlong getUidRxPackets(JNIEnv* env, jobject clazz, jint uid) {
    260     return getUidPkts(env, clazz, uid, RX, TCP_AND_UDP);
    261 }
    262 
    263 static jlong getUidTcpTxBytes(JNIEnv* env, jobject clazz, jint uid) {
    264     return getUidBytes(env, clazz, uid, TX, TCP);
    265 }
    266 
    267 static jlong getUidTcpRxBytes(JNIEnv* env, jobject clazz, jint uid) {
    268     return getUidBytes(env, clazz, uid, RX, TCP);
    269 }
    270 
    271 static jlong getUidUdpTxBytes(JNIEnv* env, jobject clazz, jint uid) {
    272     return getUidBytes(env, clazz, uid, TX, UDP);
    273 }
    274 
    275 static jlong getUidUdpRxBytes(JNIEnv* env, jobject clazz, jint uid) {
    276     return getUidBytes(env, clazz, uid, RX, UDP);
    277 }
    278 
    279 static jlong getUidTcpTxSegments(JNIEnv* env, jobject clazz, jint uid) {
    280     return getUidPkts(env, clazz, uid, TX, TCP);
    281 }
    282 
    283 static jlong getUidTcpRxSegments(JNIEnv* env, jobject clazz, jint uid) {
    284     return getUidPkts(env, clazz, uid, RX, TCP);
    285 }
    286 
    287 static jlong getUidUdpTxPackets(JNIEnv* env, jobject clazz, jint uid) {
    288     return getUidPkts(env, clazz, uid, TX, UDP);
    289 }
    290 
    291 static jlong getUidUdpRxPackets(JNIEnv* env, jobject clazz, jint uid) {
    292     return getUidPkts(env, clazz, uid, RX, UDP);
    293 }
    294 
    295 static 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 
    316 int register_android_net_TrafficStats(JNIEnv* env) {
    317     return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats",
    318             gMethods, NELEM(gMethods));
    319 }
    320 
    321 }
    322