blob: c22b07138ca301fe2df79311013196eb5660ef66 [file] [log] [blame]
Dan Egnor2b4abcd2010-04-07 17:30:50 -07001/*
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>
Elliott Hughesf17b9712011-04-12 16:12:09 -070028#include <ScopedUtfChars.h>
Dan Egnor2b4abcd2010-04-07 17:30:50 -070029#include <utils/misc.h>
30#include <utils/Log.h>
31
32namespace android {
33
Ashish Sharmac39c1d42011-01-27 15:52:38 -080034enum Tx_Rx {
35 TX,
36 RX
37};
38
39enum Tcp_Udp {
40 TCP,
41 UDP,
42 TCP_AND_UDP
43};
44
Dan Egnor2b4abcd2010-04-07 17:30:50 -070045// 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
Kazuhiro Ondo64ba5ea2011-07-05 16:12:10 -050066static 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;
Dan Egnor2b4abcd2010-04-07 17:30:50 -070095}
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;
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700121}
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) {
Kazuhiro Ondo64ba5ea2011-07-05 16:12:10 -0500128 return getAll(mobile_iface_list, "tx_packets");
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700129}
130
131static jlong getMobileRxPackets(JNIEnv* env, jobject clazz) {
Kazuhiro Ondo64ba5ea2011-07-05 16:12:10 -0500132 return getAll(mobile_iface_list, "rx_packets");
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700133}
134
135static jlong getMobileTxBytes(JNIEnv* env, jobject clazz) {
Kazuhiro Ondo64ba5ea2011-07-05 16:12:10 -0500136 return getAll(mobile_iface_list, "tx_bytes");
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700137}
138
139static jlong getMobileRxBytes(JNIEnv* env, jobject clazz) {
Kazuhiro Ondo64ba5ea2011-07-05 16:12:10 -0500140 return getAll(mobile_iface_list, "rx_bytes");
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700141}
142
Elliott Hughesf17b9712011-04-12 16:12:09 -0700143static 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
Irfan Sheriff227bec42011-02-15 19:30:27 -0800149 char filename[80];
Elliott Hughesf17b9712011-04-12 16:12:09 -0700150 snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interface.c_str(), what);
Irfan Sheriff227bec42011-02-15 19:30:27 -0800151 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
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700171// 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
Ashish Sharmac39c1d42011-01-27 15:52:38 -0800192static 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
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700274static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) {
Ashish Sharmac39c1d42011-01-27 15:52:38 -0800275 return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP);
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700276}
277
278static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) {
Ashish Sharmac39c1d42011-01-27 15:52:38 -0800279 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);
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700322}
323
324static JNINativeMethod gMethods[] = {
325 {"getMobileTxPackets", "()J", (void*) getMobileTxPackets},
326 {"getMobileRxPackets", "()J", (void*) getMobileRxPackets},
327 {"getMobileTxBytes", "()J", (void*) getMobileTxBytes},
328 {"getMobileRxBytes", "()J", (void*) getMobileRxBytes},
Irfan Sheriff227bec42011-02-15 19:30:27 -0800329 {"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},
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700333 {"getTotalTxPackets", "()J", (void*) getTotalTxPackets},
334 {"getTotalRxPackets", "()J", (void*) getTotalRxPackets},
335 {"getTotalTxBytes", "()J", (void*) getTotalTxBytes},
336 {"getTotalRxBytes", "()J", (void*) getTotalRxBytes},
Ashish Sharmac39c1d42011-01-27 15:52:38 -0800337
338 /* Per-UID Stats */
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700339 {"getUidTxBytes", "(I)J", (void*) getUidTxBytes},
340 {"getUidRxBytes", "(I)J", (void*) getUidRxBytes},
Ashish Sharmac39c1d42011-01-27 15:52:38 -0800341 {"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},
Dan Egnor2b4abcd2010-04-07 17:30:50 -0700353};
354
355int register_android_net_TrafficStats(JNIEnv* env) {
356 return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats",
357 gMethods, NELEM(gMethods));
358}
359
360}