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