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