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