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