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
34static const uint64_t VALUE_UNKNOWN = -1;
35static const char* IFACE_STAT_ALL = "/proc/net/xt_qtaguid/iface_stat_all";
36
37enum Tx_Rx {
38    TX,
39    RX
40};
41
42enum Tcp_Udp {
43    TCP,
44    UDP,
45    TCP_AND_UDP
46};
47
48// NOTE: keep these in sync with TrafficStats.java
49enum IfaceStatType {
50    RX_BYTES = 0,
51    RX_PACKETS = 1,
52    TX_BYTES = 2,
53    TX_PACKETS = 3
54};
55
56struct IfaceStat {
57    uint64_t rxBytes;
58    uint64_t rxPackets;
59    uint64_t txBytes;
60    uint64_t txPackets;
61};
62
63// Returns an ASCII decimal number read from the specified file, -1 on error.
64static jlong readNumber(char const* filename) {
65    char buf[80];
66    int fd = open(filename, O_RDONLY);
67    if (fd < 0) {
68        if (errno != ENOENT) ALOGE("Can't open %s: %s", filename, strerror(errno));
69        return -1;
70    }
71
72    int len = read(fd, buf, sizeof(buf) - 1);
73    if (len < 0) {
74        ALOGE("Can't read %s: %s", filename, strerror(errno));
75        close(fd);
76        return -1;
77    }
78
79    close(fd);
80    buf[len] = '\0';
81    return atoll(buf);
82}
83
84static int parseIfaceStat(const char* iface, struct IfaceStat* stat) {
85    FILE *fp = fopen(IFACE_STAT_ALL, "r");
86    if (!fp) {
87        return errno;
88    }
89
90    char buffer[256];
91    char cur_iface[32];
92    int active;
93    uint64_t rxBytes, rxPackets, txBytes, txPackets, devRxBytes, devRxPackets, devTxBytes,
94            devTxPackets;
95
96    while (fgets(buffer, 256, fp) != NULL) {
97        if (sscanf(buffer, "%31s %d %llu %llu %llu %llu %llu %llu %llu %llu", cur_iface, &active,
98                   &rxBytes, &rxPackets, &txBytes, &txPackets, &devRxBytes, &devRxPackets,
99                   &devTxBytes, &devTxPackets) != 10) {
100            continue;
101        }
102
103        if (!iface || !strcmp(iface, cur_iface)) {
104            stat->rxBytes += rxBytes;
105            stat->rxPackets += rxPackets;
106            stat->txBytes += txBytes;
107            stat->txPackets += txPackets;
108
109            if (active) {
110                stat->rxBytes += devRxBytes;
111                stat->rxPackets += devRxPackets;
112                stat->txBytes += devTxBytes;
113                stat->txPackets += devTxPackets;
114            }
115        }
116    }
117
118    fclose(fp);
119    return 0;
120}
121
122static uint64_t getIfaceStatType(const char* iface, IfaceStatType type) {
123    struct IfaceStat stat;
124    memset(&stat, 0, sizeof(IfaceStat));
125
126    if (parseIfaceStat(iface, &stat)) {
127        return VALUE_UNKNOWN;
128    }
129
130    switch (type) {
131        case RX_BYTES:
132            return stat.rxBytes;
133        case RX_PACKETS:
134            return stat.rxPackets;
135        case TX_BYTES:
136            return stat.txBytes;
137        case TX_PACKETS:
138            return stat.txPackets;
139        default:
140            return VALUE_UNKNOWN;
141    }
142}
143
144static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
145    return getIfaceStatType(NULL, (IfaceStatType) type);
146}
147
148static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
149    struct IfaceStat stat;
150    const char* ifaceChars = env->GetStringUTFChars(iface, NULL);
151    if (ifaceChars) {
152        uint64_t stat = getIfaceStatType(ifaceChars, (IfaceStatType) type);
153        env->ReleaseStringUTFChars(iface, ifaceChars);
154        return stat;
155    } else {
156        return VALUE_UNKNOWN;
157    }
158}
159
160
161// Per-UID stats require reading from a constructed filename.
162
163static jlong getUidBytes(JNIEnv* env, jobject clazz, jint uid,
164                         enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) {
165    char tcp_filename[80], udp_filename[80];
166    jlong tcp_bytes = -1, udp_bytes = -1, total_bytes = -1;
167
168    switch (tx_or_rx) {
169        case TX:
170            sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd", uid);
171            sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd", uid);
172            break;
173        case RX:
174            sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv", uid);
175            sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv", uid);
176            break;
177        default:
178            return -1;
179    }
180
181    switch (tcp_or_udp) {
182        case TCP:
183            tcp_bytes = readNumber(tcp_filename);
184            total_bytes = (tcp_bytes >= 0) ? tcp_bytes : -1;
185            break;
186        case UDP:
187            udp_bytes = readNumber(udp_filename);
188            total_bytes = (udp_bytes >= 0) ? udp_bytes : -1;
189            break;
190        case TCP_AND_UDP:
191            tcp_bytes = readNumber(tcp_filename);
192            total_bytes += (tcp_bytes >= 0 ? tcp_bytes : 0);
193
194            udp_bytes = readNumber(udp_filename);
195            total_bytes += (udp_bytes >= 0 ? udp_bytes : 0);
196            break;
197        default:
198            return -1;
199    }
200
201    return total_bytes;
202}
203
204static jlong getUidPkts(JNIEnv* env, jobject clazz, jint uid,
205                         enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) {
206    char tcp_filename[80], udp_filename[80];
207    jlong tcp_pkts = -1, udp_pkts = -1, total_pkts = -1;
208
209    switch (tx_or_rx) {
210        case TX:
211            sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd_pkt", uid);
212            sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd_pkt", uid);
213            break;
214        case RX:
215            sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv_pkt", uid);
216            sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv_pkt", uid);
217            break;
218        default:
219            return -1;
220    }
221
222    switch (tcp_or_udp) {
223        case TCP:
224            tcp_pkts = readNumber(tcp_filename);
225            total_pkts = (tcp_pkts >= 0) ? tcp_pkts : -1;
226            break;
227        case UDP:
228            udp_pkts = readNumber(udp_filename);
229            total_pkts = (udp_pkts >= 0) ? udp_pkts : -1;
230            break;
231        case TCP_AND_UDP:
232            tcp_pkts = readNumber(tcp_filename);
233            total_pkts += (tcp_pkts >= 0 ? tcp_pkts : 0);
234
235            udp_pkts = readNumber(udp_filename);
236            total_pkts += (udp_pkts >= 0 ? udp_pkts : 0);
237            break;
238        default:
239            return -1;
240    }
241
242    return total_pkts;
243}
244
245static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) {
246    return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP);
247}
248
249static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) {
250    return getUidBytes(env, clazz, uid, TX, TCP_AND_UDP);
251}
252
253/* TCP Segments + UDP Packets */
254static jlong getUidTxPackets(JNIEnv* env, jobject clazz, jint uid) {
255    return getUidPkts(env, clazz, uid, TX, TCP_AND_UDP);
256}
257
258/* TCP Segments + UDP Packets */
259static jlong getUidRxPackets(JNIEnv* env, jobject clazz, jint uid) {
260    return getUidPkts(env, clazz, uid, RX, TCP_AND_UDP);
261}
262
263static jlong getUidTcpTxBytes(JNIEnv* env, jobject clazz, jint uid) {
264    return getUidBytes(env, clazz, uid, TX, TCP);
265}
266
267static jlong getUidTcpRxBytes(JNIEnv* env, jobject clazz, jint uid) {
268    return getUidBytes(env, clazz, uid, RX, TCP);
269}
270
271static jlong getUidUdpTxBytes(JNIEnv* env, jobject clazz, jint uid) {
272    return getUidBytes(env, clazz, uid, TX, UDP);
273}
274
275static jlong getUidUdpRxBytes(JNIEnv* env, jobject clazz, jint uid) {
276    return getUidBytes(env, clazz, uid, RX, UDP);
277}
278
279static jlong getUidTcpTxSegments(JNIEnv* env, jobject clazz, jint uid) {
280    return getUidPkts(env, clazz, uid, TX, TCP);
281}
282
283static jlong getUidTcpRxSegments(JNIEnv* env, jobject clazz, jint uid) {
284    return getUidPkts(env, clazz, uid, RX, TCP);
285}
286
287static jlong getUidUdpTxPackets(JNIEnv* env, jobject clazz, jint uid) {
288    return getUidPkts(env, clazz, uid, TX, UDP);
289}
290
291static jlong getUidUdpRxPackets(JNIEnv* env, jobject clazz, jint uid) {
292    return getUidPkts(env, clazz, uid, RX, UDP);
293}
294
295static JNINativeMethod gMethods[] = {
296    {"nativeGetTotalStat", "(I)J", (void*) getTotalStat},
297    {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat},
298
299    /* Per-UID Stats */
300    {"getUidTxBytes", "(I)J", (void*) getUidTxBytes},
301    {"getUidRxBytes", "(I)J", (void*) getUidRxBytes},
302    {"getUidTxPackets", "(I)J", (void*) getUidTxPackets},
303    {"getUidRxPackets", "(I)J", (void*) getUidRxPackets},
304
305    {"getUidTcpTxBytes", "(I)J", (void*) getUidTcpTxBytes},
306    {"getUidTcpRxBytes", "(I)J", (void*) getUidTcpRxBytes},
307    {"getUidUdpTxBytes", "(I)J", (void*) getUidUdpTxBytes},
308    {"getUidUdpRxBytes", "(I)J", (void*) getUidUdpRxBytes},
309
310    {"getUidTcpTxSegments", "(I)J", (void*) getUidTcpTxSegments},
311    {"getUidTcpRxSegments", "(I)J", (void*) getUidTcpRxSegments},
312    {"getUidUdpTxPackets", "(I)J", (void*) getUidUdpTxPackets},
313    {"getUidUdpRxPackets", "(I)J", (void*) getUidUdpRxPackets},
314};
315
316int register_android_net_TrafficStats(JNIEnv* env) {
317    return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats",
318            gMethods, NELEM(gMethods));
319}
320
321}
322