android_net_TrafficStats.cpp revision 4e8d8238e10d168b434dcdd2e779a9aab37f1e2c
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
33// Returns an ASCII decimal number read from the specified file, -1 on error.
34static jlong readNumber(char const* filename) {
35#ifdef HAVE_ANDROID_OS
36    char buf[80];
37    int fd = open(filename, O_RDONLY);
38    if (fd < 0) {
39        if (errno != ENOENT) LOGE("Can't open %s: %s", filename, strerror(errno));
40        return -1;
41    }
42
43    int len = read(fd, buf, sizeof(buf) - 1);
44    if (len < 0) {
45        LOGE("Can't read %s: %s", filename, strerror(errno));
46        close(fd);
47        return -1;
48    }
49
50    close(fd);
51    buf[len] = '\0';
52    return atoll(buf);
53#else  // Simulator
54    return -1;
55#endif
56}
57
58// Return the number from the first file which exists and contains data
59static jlong tryBoth(char const* a, char const* b) {
60    jlong num = readNumber(a);
61    return num >= 0 ? num : readNumber(b);
62}
63
64// Returns the sum of numbers from the specified path under /sys/class/net/*,
65// -1 if no such file exists.
66static jlong readTotal(char const* suffix) {
67#ifdef HAVE_ANDROID_OS
68    char filename[PATH_MAX] = "/sys/class/net/";
69    DIR *dir = opendir(filename);
70    if (dir == NULL) {
71        LOGE("Can't list %s: %s", filename, strerror(errno));
72        return -1;
73    }
74
75    int len = strlen(filename);
76    jlong total = -1;
77    while (struct dirent *entry = readdir(dir)) {
78        // Skip ., .., and localhost interfaces.
79        if (entry->d_name[0] != '.' && strncmp(entry->d_name, "lo", 2) != 0) {
80            strlcpy(filename + len, entry->d_name, sizeof(filename) - len);
81            strlcat(filename, suffix, sizeof(filename));
82            jlong num = readNumber(filename);
83            if (num >= 0) total = total < 0 ? num : total + num;
84        }
85    }
86
87    closedir(dir);
88    return total;
89#else  // Simulator
90    return -1;
91#endif
92}
93
94// Mobile stats get accessed a lot more often than total stats.
95// Note the individual files can come and go at runtime, so we check
96// each file every time (rather than caching which ones exist).
97
98static jlong getMobileTxPackets(JNIEnv* env, jobject clazz) {
99    return tryBoth(
100            "/sys/class/net/rmnet0/statistics/tx_packets",
101            "/sys/class/net/ppp0/statistics/tx_packets");
102}
103
104static jlong getMobileRxPackets(JNIEnv* env, jobject clazz) {
105    return tryBoth(
106            "/sys/class/net/rmnet0/statistics/rx_packets",
107            "/sys/class/net/ppp0/statistics/rx_packets");
108}
109
110static jlong getMobileTxBytes(JNIEnv* env, jobject clazz) {
111    return tryBoth(
112            "/sys/class/net/rmnet0/statistics/tx_bytes",
113            "/sys/class/net/ppp0/statistics/tx_bytes");
114}
115
116static jlong getMobileRxBytes(JNIEnv* env, jobject clazz) {
117    return tryBoth(
118            "/sys/class/net/rmnet0/statistics/rx_bytes",
119            "/sys/class/net/ppp0/statistics/rx_bytes");
120}
121
122// Total stats are read less often, so we're willing to put up
123// with listing the directory and concatenating filenames.
124
125static jlong getTotalTxPackets(JNIEnv* env, jobject clazz) {
126    return readTotal("/statistics/tx_packets");
127}
128
129static jlong getTotalRxPackets(JNIEnv* env, jobject clazz) {
130    return readTotal("/statistics/rx_packets");
131}
132
133static jlong getTotalTxBytes(JNIEnv* env, jobject clazz) {
134    return readTotal("/statistics/tx_bytes");
135}
136
137static jlong getTotalRxBytes(JNIEnv* env, jobject clazz) {
138    return readTotal("/statistics/rx_bytes");
139}
140
141// Per-UID stats require reading from a constructed filename.
142
143static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) {
144    char filename[80];
145    sprintf(filename, "/proc/uid_stat/%d/tcp_rcv", uid);
146    return readNumber(filename);
147}
148
149static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) {
150    char filename[80];
151    sprintf(filename, "/proc/uid_stat/%d/tcp_snd", uid);
152    return readNumber(filename);
153}
154
155static JNINativeMethod gMethods[] = {
156    {"getMobileTxPackets", "()J", (void*) getMobileTxPackets},
157    {"getMobileRxPackets", "()J", (void*) getMobileRxPackets},
158    {"getMobileTxBytes", "()J", (void*) getMobileTxBytes},
159    {"getMobileRxBytes", "()J", (void*) getMobileRxBytes},
160    {"getTotalTxPackets", "()J", (void*) getTotalTxPackets},
161    {"getTotalRxPackets", "()J", (void*) getTotalRxPackets},
162    {"getTotalTxBytes", "()J", (void*) getTotalTxBytes},
163    {"getTotalRxBytes", "()J", (void*) getTotalRxBytes},
164    {"getUidTxBytes", "(I)J", (void*) getUidTxBytes},
165    {"getUidRxBytes", "(I)J", (void*) getUidRxBytes},
166};
167
168int register_android_net_TrafficStats(JNIEnv* env) {
169    return AndroidRuntime::registerNativeMethods(env, "android/net/TrafficStats",
170            gMethods, NELEM(gMethods));
171}
172
173}
174