android_net_TrafficStats.cpp revision c39c1d4dee917560d174f6ba5402e4c6644edd47
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
133// Total stats are read less often, so we're willing to put up
134// with listing the directory and concatenating filenames.
135
136static jlong getTotalTxPackets(JNIEnv* env, jobject clazz) {
137    return readTotal("/statistics/tx_packets");
138}
139
140static jlong getTotalRxPackets(JNIEnv* env, jobject clazz) {
141    return readTotal("/statistics/rx_packets");
142}
143
144static jlong getTotalTxBytes(JNIEnv* env, jobject clazz) {
145    return readTotal("/statistics/tx_bytes");
146}
147
148static jlong getTotalRxBytes(JNIEnv* env, jobject clazz) {
149    return readTotal("/statistics/rx_bytes");
150}
151
152// Per-UID stats require reading from a constructed filename.
153
154static jlong getUidBytes(JNIEnv* env, jobject clazz, jint uid,
155                         enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) {
156    char tcp_filename[80], udp_filename[80];
157    jlong tcp_bytes = -1, udp_bytes = -1, total_bytes = -1;
158
159    switch (tx_or_rx) {
160        case TX:
161            sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd", uid);
162            sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd", uid);
163            break;
164        case RX:
165            sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv", uid);
166            sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv", uid);
167            break;
168        default:
169            return -1;
170    }
171
172    switch (tcp_or_udp) {
173        case TCP:
174            tcp_bytes = readNumber(tcp_filename);
175            total_bytes = (tcp_bytes >= 0) ? tcp_bytes : -1;
176            break;
177        case UDP:
178            udp_bytes = readNumber(udp_filename);
179            total_bytes = (udp_bytes >= 0) ? udp_bytes : -1;
180            break;
181        case TCP_AND_UDP:
182            tcp_bytes = readNumber(tcp_filename);
183            total_bytes += (tcp_bytes >= 0 ? tcp_bytes : 0);
184
185            udp_bytes = readNumber(udp_filename);
186            total_bytes += (udp_bytes >= 0 ? udp_bytes : 0);
187            break;
188        default:
189            return -1;
190    }
191
192    return total_bytes;
193}
194
195static jlong getUidPkts(JNIEnv* env, jobject clazz, jint uid,
196                         enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) {
197    char tcp_filename[80], udp_filename[80];
198    jlong tcp_pkts = -1, udp_pkts = -1, total_pkts = -1;
199
200    switch (tx_or_rx) {
201        case TX:
202            sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd_pkt", uid);
203            sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd_pkt", uid);
204            break;
205        case RX:
206            sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv_pkt", uid);
207            sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv_pkt", uid);
208            break;
209        default:
210            return -1;
211    }
212
213    switch (tcp_or_udp) {
214        case TCP:
215            tcp_pkts = readNumber(tcp_filename);
216            total_pkts = (tcp_pkts >= 0) ? tcp_pkts : -1;
217            break;
218        case UDP:
219            udp_pkts = readNumber(udp_filename);
220            total_pkts = (udp_pkts >= 0) ? udp_pkts : -1;
221            break;
222        case TCP_AND_UDP:
223            tcp_pkts = readNumber(tcp_filename);
224            total_pkts += (tcp_pkts >= 0 ? tcp_pkts : 0);
225
226            udp_pkts = readNumber(udp_filename);
227            total_pkts += (udp_pkts >= 0 ? udp_pkts : 0);
228            break;
229        default:
230            return -1;
231    }
232
233    return total_pkts;
234}
235
236static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) {
237    return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP);
238}
239
240static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) {
241    return getUidBytes(env, clazz, uid, TX, TCP_AND_UDP);
242}
243
244/* TCP Segments + UDP Packets */
245static jlong getUidTxPackets(JNIEnv* env, jobject clazz, jint uid) {
246    return getUidPkts(env, clazz, uid, TX, TCP_AND_UDP);
247}
248
249/* TCP Segments + UDP Packets */
250static jlong getUidRxPackets(JNIEnv* env, jobject clazz, jint uid) {
251    return getUidPkts(env, clazz, uid, RX, TCP_AND_UDP);
252}
253
254static jlong getUidTcpTxBytes(JNIEnv* env, jobject clazz, jint uid) {
255    return getUidBytes(env, clazz, uid, TX, TCP);
256}
257
258static jlong getUidTcpRxBytes(JNIEnv* env, jobject clazz, jint uid) {
259    return getUidBytes(env, clazz, uid, RX, TCP);
260}
261
262static jlong getUidUdpTxBytes(JNIEnv* env, jobject clazz, jint uid) {
263    return getUidBytes(env, clazz, uid, TX, UDP);
264}
265
266static jlong getUidUdpRxBytes(JNIEnv* env, jobject clazz, jint uid) {
267    return getUidBytes(env, clazz, uid, RX, UDP);
268}
269
270static jlong getUidTcpTxSegments(JNIEnv* env, jobject clazz, jint uid) {
271    return getUidPkts(env, clazz, uid, TX, TCP);
272}
273
274static jlong getUidTcpRxSegments(JNIEnv* env, jobject clazz, jint uid) {
275    return getUidPkts(env, clazz, uid, RX, TCP);
276}
277
278static jlong getUidUdpTxPackets(JNIEnv* env, jobject clazz, jint uid) {
279    return getUidPkts(env, clazz, uid, TX, UDP);
280}
281
282static jlong getUidUdpRxPackets(JNIEnv* env, jobject clazz, jint uid) {
283    return getUidPkts(env, clazz, uid, RX, UDP);
284}
285
286static JNINativeMethod gMethods[] = {
287    {"getMobileTxPackets", "()J", (void*) getMobileTxPackets},
288    {"getMobileRxPackets", "()J", (void*) getMobileRxPackets},
289    {"getMobileTxBytes", "()J", (void*) getMobileTxBytes},
290    {"getMobileRxBytes", "()J", (void*) getMobileRxBytes},
291    {"getTotalTxPackets", "()J", (void*) getTotalTxPackets},
292    {"getTotalRxPackets", "()J", (void*) getTotalRxPackets},
293    {"getTotalTxBytes", "()J", (void*) getTotalTxBytes},
294    {"getTotalRxBytes", "()J", (void*) getTotalRxBytes},
295
296    /* Per-UID Stats */
297    {"getUidTxBytes", "(I)J", (void*) getUidTxBytes},
298    {"getUidRxBytes", "(I)J", (void*) getUidRxBytes},
299    {"getUidTxPackets", "(I)J", (void*) getUidTxPackets},
300    {"getUidRxPackets", "(I)J", (void*) getUidRxPackets},
301
302    {"getUidTcpTxBytes", "(I)J", (void*) getUidTcpTxBytes},
303    {"getUidTcpRxBytes", "(I)J", (void*) getUidTcpRxBytes},
304    {"getUidUdpTxBytes", "(I)J", (void*) getUidUdpTxBytes},
305    {"getUidUdpRxBytes", "(I)J", (void*) getUidUdpRxBytes},
306
307    {"getUidTcpTxSegments", "(I)J", (void*) getUidTcpTxSegments},
308    {"getUidTcpRxSegments", "(I)J", (void*) getUidTcpRxSegments},
309    {"getUidUdpTxPackets", "(I)J", (void*) getUidUdpTxPackets},
310    {"getUidUdpRxPackets", "(I)J", (void*) getUidUdpRxPackets},
311};
312
313int register_android_net_TrafficStats(JNIEnv* env) {
314    return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats",
315            gMethods, NELEM(gMethods));
316}
317
318}
319