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