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