blob: 109e65c4a1d044fd844f2f3cc8524691ab496a0f [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
Chenbo Fengdcc56782018-04-18 15:44:46 -0700208static int statsLinesToNetworkStats(JNIEnv* env, jclass clazz, jobject stats,
209 std::vector<stats_line>& lines) {
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800210 int size = lines.size();
Chenbo Feng828f1b42017-11-20 17:03:59 -0800211
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800212 bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity);
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800213
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800214 ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats,
215 gNetworkStatsClassInfo.iface, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800216 if (iface.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800217 ScopedIntArrayRW uid(env, get_int_array(env, stats,
218 gNetworkStatsClassInfo.uid, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800219 if (uid.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800220 ScopedIntArrayRW set(env, get_int_array(env, stats,
221 gNetworkStatsClassInfo.set, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800222 if (set.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800223 ScopedIntArrayRW tag(env, get_int_array(env, stats,
224 gNetworkStatsClassInfo.tag, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800225 if (tag.get() == NULL) return -1;
Stephen Chenc926b732016-10-21 12:44:26 -0700226 ScopedIntArrayRW metered(env, get_int_array(env, stats,
227 gNetworkStatsClassInfo.metered, size, grow));
228 if (metered.get() == NULL) return -1;
Jeff Davidsona6a78072016-01-11 16:02:17 -0800229 ScopedIntArrayRW roaming(env, get_int_array(env, stats,
230 gNetworkStatsClassInfo.roaming, size, grow));
231 if (roaming.get() == NULL) return -1;
Lorenzo Colittiada23ed2018-01-19 01:05:20 +0900232 ScopedIntArrayRW defaultNetwork(env, get_int_array(env, stats,
233 gNetworkStatsClassInfo.defaultNetwork, size, grow));
234 if (defaultNetwork.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800235 ScopedLongArrayRW rxBytes(env, get_long_array(env, stats,
236 gNetworkStatsClassInfo.rxBytes, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800237 if (rxBytes.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800238 ScopedLongArrayRW rxPackets(env, get_long_array(env, stats,
239 gNetworkStatsClassInfo.rxPackets, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800240 if (rxPackets.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800241 ScopedLongArrayRW txBytes(env, get_long_array(env, stats,
242 gNetworkStatsClassInfo.txBytes, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800243 if (txBytes.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800244 ScopedLongArrayRW txPackets(env, get_long_array(env, stats,
245 gNetworkStatsClassInfo.txPackets, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800246 if (txPackets.get() == NULL) return -1;
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800247 ScopedLongArrayRW operations(env, get_long_array(env, stats,
248 gNetworkStatsClassInfo.operations, size, grow));
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800249 if (operations.get() == NULL) return -1;
250
251 for (int i = 0; i < size; i++) {
252 ScopedLocalRef<jstring> ifaceString(env, env->NewStringUTF(lines[i].iface));
253 env->SetObjectArrayElement(iface.get(), i, ifaceString.get());
254
255 uid[i] = lines[i].uid;
256 set[i] = lines[i].set;
257 tag[i] = lines[i].tag;
Lorenzo Colittiada23ed2018-01-19 01:05:20 +0900258 // Metered, roaming and defaultNetwork are populated in Java-land.
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800259 rxBytes[i] = lines[i].rxBytes;
260 rxPackets[i] = lines[i].rxPackets;
261 txBytes[i] = lines[i].txBytes;
262 txPackets[i] = lines[i].txPackets;
263 }
264
265 env->SetIntField(stats, gNetworkStatsClassInfo.size, size);
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800266 if (grow) {
267 env->SetIntField(stats, gNetworkStatsClassInfo.capacity, size);
268 env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get());
269 env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray());
270 env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray());
271 env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
Stephen Chenc926b732016-10-21 12:44:26 -0700272 env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray());
Jeff Davidsona6a78072016-01-11 16:02:17 -0800273 env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray());
Lorenzo Colittiada23ed2018-01-19 01:05:20 +0900274 env->SetObjectField(stats, gNetworkStatsClassInfo.defaultNetwork,
275 defaultNetwork.getJavaArray());
Dianne Hackbornd0c5b9a2014-02-21 16:19:05 -0800276 env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
277 env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
278 env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
279 env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray());
280 env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray());
281 }
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800282 return 0;
283}
284
Chenbo Fengdcc56782018-04-18 15:44:46 -0700285static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
286 jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
287 jboolean useBpfStats) {
Chenbo Fengdcc56782018-04-18 15:44:46 -0700288
289 std::vector<std::string> limitIfaces;
290 if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
291 int num = env->GetArrayLength(limitIfacesObj);
292 for (int i = 0; i < num; i++) {
293 jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
294 ScopedUtfChars string8(env, string);
295 if (string8.c_str() != NULL) {
296 limitIfaces.push_back(std::string(string8.c_str()));
297 }
298 }
299 }
300 std::vector<stats_line> lines;
301
302
303 if (useBpfStats) {
304 if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
305 return -1;
306 } else {
Chenbo Fengf1ef0a82018-09-14 15:16:48 -0700307 ScopedUtfChars path8(env, path);
308 if (path8.c_str() == NULL) {
309 ALOGE("the qtaguid legacy path is invalid: %s", path8.c_str());
310 return -1;
311 }
Chenbo Fengdcc56782018-04-18 15:44:46 -0700312 if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
313 limitUid, path8.c_str()) < 0)
314 return -1;
315 }
316
317 return statsLinesToNetworkStats(env, clazz, stats, lines);
318}
319
320static int readNetworkStatsDev(JNIEnv* env, jclass clazz, jobject stats) {
321 std::vector<stats_line> lines;
322
323 if (parseBpfNetworkStatsDev(&lines) < 0)
324 return -1;
325
326 return statsLinesToNetworkStats(env, clazz, stats, lines);
327}
328
Daniel Micay76f6a862015-09-19 17:31:01 -0400329static const JNINativeMethod gMethods[] = {
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800330 { "nativeReadNetworkStatsDetail",
Chenbo Feng828f1b42017-11-20 17:03:59 -0800331 "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;IZ)I",
Chenbo Fengdcc56782018-04-18 15:44:46 -0700332 (void*) readNetworkStatsDetail },
333 { "nativeReadNetworkStatsDev", "(Landroid/net/NetworkStats;)I",
334 (void*) readNetworkStatsDev },
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800335};
336
337int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800338 int err = RegisterMethodsOrDie(env,
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800339 "com/android/internal/net/NetworkStatsFactory", gMethods,
340 NELEM(gMethods));
341
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800342 gStringClass = FindClassOrDie(env, "java/lang/String");
343 gStringClass = MakeGlobalRefOrDie(env, gStringClass);
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800344
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800345 jclass clazz = FindClassOrDie(env, "android/net/NetworkStats");
346 gNetworkStatsClassInfo.size = GetFieldIDOrDie(env, clazz, "size", "I");
347 gNetworkStatsClassInfo.capacity = GetFieldIDOrDie(env, clazz, "capacity", "I");
348 gNetworkStatsClassInfo.iface = GetFieldIDOrDie(env, clazz, "iface", "[Ljava/lang/String;");
349 gNetworkStatsClassInfo.uid = GetFieldIDOrDie(env, clazz, "uid", "[I");
350 gNetworkStatsClassInfo.set = GetFieldIDOrDie(env, clazz, "set", "[I");
351 gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I");
Stephen Chenc926b732016-10-21 12:44:26 -0700352 gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I");
Jeff Davidsona6a78072016-01-11 16:02:17 -0800353 gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I");
Lorenzo Colittiada23ed2018-01-19 01:05:20 +0900354 gNetworkStatsClassInfo.defaultNetwork = GetFieldIDOrDie(env, clazz, "defaultNetwork", "[I");
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800355 gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J");
356 gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J");
357 gNetworkStatsClassInfo.txBytes = GetFieldIDOrDie(env, clazz, "txBytes", "[J");
358 gNetworkStatsClassInfo.txPackets = GetFieldIDOrDie(env, clazz, "txPackets", "[J");
359 gNetworkStatsClassInfo.operations = GetFieldIDOrDie(env, clazz, "operations", "[J");
Jeff Sharkey9a2c2a62013-01-14 16:48:51 -0800360
361 return err;
362}
363
364}