blob: 99d983957075aa3a21892beb7e200ca60a83db8f [file] [log] [blame]
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -08001/*
2 * Copyright (C) 2013 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 "NetworkStats"
18
19#include <errno.h>
Andreas Gampe0f0b4912014-11-12 08:03:48 -080020#include <inttypes.h>
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -080021#include <sys/stat.h>
22#include <sys/types.h>
23
Andreas Gampeed6b9df2014-11-20 22:02:20 -080024#include <core_jni_helpers.h>
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -080025#include <jni.h>
26
Steven Moreland2279b252017-07-19 09:50:45 -070027#include <nativehelper/ScopedUtfChars.h>
28#include <nativehelper/ScopedLocalRef.h>
29#include <nativehelper/ScopedPrimitiveArray.h>
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -080030
31#include <utils/Log.h>
32#include <utils/misc.h>
Chenbo Feng828f1b42017-11-20 17:03:59 -080033
34#include "android-base/unique_fd.h"
35#include "bpf/BpfNetworkStats.h"
36#include "bpf/BpfUtils.h"
37
38using android::bpf::hasBpfSupport;
39using android::bpf::parseBpfNetworkStatsDetail;
40using android::bpf::stats_line;
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -080041
42namespace android {
43
44static jclass gStringClass;
45
46static struct {
47 jfieldID size;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -080048 jfieldID capacity;
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -080049 jfieldID iface;
50 jfieldID uid;
51 jfieldID set;
52 jfieldID tag;
Stephen Chenc926b732016-10-21 12:44:26 -070053 jfieldID metered;
Jeff Davidsona6a78072016-01-11 16:02:17 -080054 jfieldID roaming;
Lorenzo Colittiada23ed2018-01-19 01:05:20 +090055 jfieldID defaultNetwork;
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -080056 jfieldID rxBytes;
57 jfieldID rxPackets;
58 jfieldID txBytes;
59 jfieldID txPackets;
60 jfieldID operations;
61} gNetworkStatsClassInfo;
62
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -080063static jobjectArray get_string_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
64{
65 if (!grow) {
66 jobjectArray array = (jobjectArray)env->GetObjectField(obj, field);
67 if (array != NULL) {
68 return array;
69 }
70 }
71 return env->NewObjectArray(size, gStringClass, NULL);
72}
73
74static jintArray get_int_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
75{
76 if (!grow) {
77 jintArray array = (jintArray)env->GetObjectField(obj, field);
78 if (array != NULL) {
79 return array;
80 }
81 }
82 return env->NewIntArray(size);
83}
84
85static jlongArray get_long_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
86{
87 if (!grow) {
88 jlongArray array = (jlongArray)env->GetObjectField(obj, field);
89 if (array != NULL) {
90 return array;
91 }
92 }
93 return env->NewLongArray(size);
94}
95
Chenbo Feng828f1b42017-11-20 17:03:59 -080096static int legacyReadNetworkStatsDetail(std::vector<stats_line>* lines,
97 const std::vector<std::string>& limitIfaces,
98 int limitTag, int limitUid, const char* path) {
99 FILE* fp = fopen(path, "r");
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800100 if (fp == NULL) {
101 return -1;
102 }
103
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800104 int lastIdx = 1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800105 int idx;
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800106 char buffer[384];
107 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
108 stats_line s;
109 int64_t rawTag;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800110 char* pos = buffer;
111 char* endPos;
112 // First field is the index.
113 idx = (int)strtol(pos, &endPos, 10);
114 //ALOGI("Index #%d: %s", idx, buffer);
115 if (pos == endPos) {
116 // Skip lines that don't start with in index. In particular,
117 // this will skip the initial header line.
118 continue;
119 }
120 if (idx != lastIdx + 1) {
121 ALOGE("inconsistent idx=%d after lastIdx=%d: %s", idx, lastIdx, buffer);
122 fclose(fp);
123 return -1;
124 }
125 lastIdx = idx;
126 pos = endPos;
127 // Skip whitespace.
128 while (*pos == ' ') {
129 pos++;
130 }
131 // Next field is iface.
132 int ifaceIdx = 0;
133 while (*pos != ' ' && *pos != 0 && ifaceIdx < (int)(sizeof(s.iface)-1)) {
134 s.iface[ifaceIdx] = *pos;
135 ifaceIdx++;
136 pos++;
137 }
138 if (*pos != ' ') {
139 ALOGE("bad iface: %s", buffer);
140 fclose(fp);
141 return -1;
142 }
143 s.iface[ifaceIdx] = 0;
144 if (limitIfaces.size() > 0) {
145 // Is this an iface the caller is interested in?
146 int i = 0;
147 while (i < (int)limitIfaces.size()) {
148 if (limitIfaces[i] == s.iface) {
149 break;
150 }
151 i++;
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800152 }
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800153 if (i >= (int)limitIfaces.size()) {
154 // Nothing matched; skip this line.
155 //ALOGI("skipping due to iface: %s", buffer);
156 continue;
157 }
158 }
Jeff Sharkeye47d5432014-09-09 12:29:41 -0700159
160 // Ignore whitespace
161 while (*pos == ' ') pos++;
162
163 // Find end of tag field
164 endPos = pos;
165 while (*endPos != ' ') endPos++;
166
167 // Three digit field is always 0x0, otherwise parse
168 if (endPos - pos == 3) {
169 rawTag = 0;
170 } else {
Andreas Gampe0f0b4912014-11-12 08:03:48 -0800171 if (sscanf(pos, "%" PRIx64, &rawTag) != 1) {
Jeff Sharkeye47d5432014-09-09 12:29:41 -0700172 ALOGE("bad tag: %s", pos);
173 fclose(fp);
174 return -1;
175 }
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800176 }
177 s.tag = rawTag >> 32;
178 if (limitTag != -1 && s.tag != limitTag) {
179 //ALOGI("skipping due to tag: %s", buffer);
180 continue;
181 }
182 pos = endPos;
Jeff Sharkeye47d5432014-09-09 12:29:41 -0700183
184 // Ignore whitespace
185 while (*pos == ' ') pos++;
186
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800187 // Parse remaining fields.
Andreas Gampe0f0b4912014-11-12 08:03:48 -0800188 if (sscanf(pos, "%u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64,
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800189 &s.uid, &s.set, &s.rxBytes, &s.rxPackets,
190 &s.txBytes, &s.txPackets) == 6) {
191 if (limitUid != -1 && limitUid != s.uid) {
192 //ALOGI("skipping due to uid: %s", buffer);
193 continue;
194 }
Chenbo Feng828f1b42017-11-20 17:03:59 -0800195 lines->push_back(s);
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800196 } else {
197 //ALOGI("skipping due to bad remaining fields: %s", pos);
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800198 }
199 }
200
201 if (fclose(fp) != 0) {
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800202 ALOGE("Failed to close netstats file");
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800203 return -1;
204 }
Chenbo Feng828f1b42017-11-20 17:03:59 -0800205 return 0;
206}
207
208static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
209 jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
210 jboolean useBpfStats) {
211 ScopedUtfChars path8(env, path);
212 if (path8.c_str() == NULL) {
213 return -1;
214 }
215
216 std::vector<std::string> limitIfaces;
217 if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
218 int num = env->GetArrayLength(limitIfacesObj);
219 for (int i = 0; i < num; i++) {
220 jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
221 ScopedUtfChars string8(env, string);
222 if (string8.c_str() != NULL) {
223 limitIfaces.push_back(std::string(string8.c_str()));
224 }
225 }
226 }
227 std::vector<stats_line> lines;
228
229
230 if (useBpfStats) {
231 if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
232 return -1;
233 } else {
234 if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
235 limitUid, path8.c_str()) < 0)
236 return -1;
237 }
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800238
239 int size = lines.size();
Chenbo Feng828f1b42017-11-20 17:03:59 -0800240
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800241 bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity);
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800242
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800243 ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats,
244 gNetworkStatsClassInfo.iface, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800245 if (iface.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800246 ScopedIntArrayRW uid(env, get_int_array(env, stats,
247 gNetworkStatsClassInfo.uid, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800248 if (uid.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800249 ScopedIntArrayRW set(env, get_int_array(env, stats,
250 gNetworkStatsClassInfo.set, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800251 if (set.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800252 ScopedIntArrayRW tag(env, get_int_array(env, stats,
253 gNetworkStatsClassInfo.tag, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800254 if (tag.get() == NULL) return -1;
Stephen Chenc926b732016-10-21 12:44:26 -0700255 ScopedIntArrayRW metered(env, get_int_array(env, stats,
256 gNetworkStatsClassInfo.metered, size, grow));
257 if (metered.get() == NULL) return -1;
Jeff Davidsona6a78072016-01-11 16:02:17 -0800258 ScopedIntArrayRW roaming(env, get_int_array(env, stats,
259 gNetworkStatsClassInfo.roaming, size, grow));
260 if (roaming.get() == NULL) return -1;
Lorenzo Colittiada23ed2018-01-19 01:05:20 +0900261 ScopedIntArrayRW defaultNetwork(env, get_int_array(env, stats,
262 gNetworkStatsClassInfo.defaultNetwork, size, grow));
263 if (defaultNetwork.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800264 ScopedLongArrayRW rxBytes(env, get_long_array(env, stats,
265 gNetworkStatsClassInfo.rxBytes, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800266 if (rxBytes.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800267 ScopedLongArrayRW rxPackets(env, get_long_array(env, stats,
268 gNetworkStatsClassInfo.rxPackets, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800269 if (rxPackets.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800270 ScopedLongArrayRW txBytes(env, get_long_array(env, stats,
271 gNetworkStatsClassInfo.txBytes, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800272 if (txBytes.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800273 ScopedLongArrayRW txPackets(env, get_long_array(env, stats,
274 gNetworkStatsClassInfo.txPackets, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800275 if (txPackets.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800276 ScopedLongArrayRW operations(env, get_long_array(env, stats,
277 gNetworkStatsClassInfo.operations, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800278 if (operations.get() == NULL) return -1;
279
280 for (int i = 0; i < size; i++) {
281 ScopedLocalRef<jstring> ifaceString(env, env->NewStringUTF(lines[i].iface));
282 env->SetObjectArrayElement(iface.get(), i, ifaceString.get());
283
284 uid[i] = lines[i].uid;
285 set[i] = lines[i].set;
286 tag[i] = lines[i].tag;
Lorenzo Colittiada23ed2018-01-19 01:05:20 +0900287 // Metered, roaming and defaultNetwork are populated in Java-land.
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800288 rxBytes[i] = lines[i].rxBytes;
289 rxPackets[i] = lines[i].rxPackets;
290 txBytes[i] = lines[i].txBytes;
291 txPackets[i] = lines[i].txPackets;
292 }
293
294 env->SetIntField(stats, gNetworkStatsClassInfo.size, size);
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800295 if (grow) {
296 env->SetIntField(stats, gNetworkStatsClassInfo.capacity, size);
297 env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get());
298 env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray());
299 env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray());
300 env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
Stephen Chenc926b732016-10-21 12:44:26 -0700301 env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray());
Jeff Davidsona6a78072016-01-11 16:02:17 -0800302 env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray());
Lorenzo Colittiada23ed2018-01-19 01:05:20 +0900303 env->SetObjectField(stats, gNetworkStatsClassInfo.defaultNetwork,
304 defaultNetwork.getJavaArray());
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800305 env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
306 env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
307 env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
308 env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray());
309 env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray());
310 }
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800311
312 return 0;
313}
314
Daniel Micay76f6a862015-09-19 17:31:01 -0400315static const JNINativeMethod gMethods[] = {
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800316 { "nativeReadNetworkStatsDetail",
Chenbo Feng828f1b42017-11-20 17:03:59 -0800317 "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;IZ)I",
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800318 (void*) readNetworkStatsDetail }
319};
320
321int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800322 int err = RegisterMethodsOrDie(env,
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800323 "com/android/internal/net/NetworkStatsFactory", gMethods,
324 NELEM(gMethods));
325
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800326 gStringClass = FindClassOrDie(env, "java/lang/String");
327 gStringClass = MakeGlobalRefOrDie(env, gStringClass);
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800328
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800329 jclass clazz = FindClassOrDie(env, "android/net/NetworkStats");
330 gNetworkStatsClassInfo.size = GetFieldIDOrDie(env, clazz, "size", "I");
331 gNetworkStatsClassInfo.capacity = GetFieldIDOrDie(env, clazz, "capacity", "I");
332 gNetworkStatsClassInfo.iface = GetFieldIDOrDie(env, clazz, "iface", "[Ljava/lang/String;");
333 gNetworkStatsClassInfo.uid = GetFieldIDOrDie(env, clazz, "uid", "[I");
334 gNetworkStatsClassInfo.set = GetFieldIDOrDie(env, clazz, "set", "[I");
335 gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I");
Stephen Chenc926b732016-10-21 12:44:26 -0700336 gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I");
Jeff Davidsona6a78072016-01-11 16:02:17 -0800337 gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I");
Lorenzo Colittiada23ed2018-01-19 01:05:20 +0900338 gNetworkStatsClassInfo.defaultNetwork = GetFieldIDOrDie(env, clazz, "defaultNetwork", "[I");
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800339 gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J");
340 gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J");
341 gNetworkStatsClassInfo.txBytes = GetFieldIDOrDie(env, clazz, "txBytes", "[J");
342 gNetworkStatsClassInfo.txPackets = GetFieldIDOrDie(env, clazz, "txPackets", "[J");
343 gNetworkStatsClassInfo.operations = GetFieldIDOrDie(env, clazz, "operations", "[J");
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800344
345 return err;
346}
347
348}