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