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