1/* 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> 20#include <inttypes.h> 21#include <sys/stat.h> 22#include <sys/types.h> 23 24#include <core_jni_helpers.h> 25#include <jni.h> 26 27#include <nativehelper/ScopedUtfChars.h> 28#include <nativehelper/ScopedLocalRef.h> 29#include <nativehelper/ScopedPrimitiveArray.h> 30 31#include <utils/Log.h> 32#include <utils/misc.h> 33 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; 41 42namespace android { 43 44static jclass gStringClass; 45 46static struct { 47 jfieldID size; 48 jfieldID capacity; 49 jfieldID iface; 50 jfieldID uid; 51 jfieldID set; 52 jfieldID tag; 53 jfieldID metered; 54 jfieldID roaming; 55 jfieldID defaultNetwork; 56 jfieldID rxBytes; 57 jfieldID rxPackets; 58 jfieldID txBytes; 59 jfieldID txPackets; 60 jfieldID operations; 61} gNetworkStatsClassInfo; 62 63static 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 96static 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"); 100 if (fp == NULL) { 101 return -1; 102 } 103 104 int lastIdx = 1; 105 int idx; 106 char buffer[384]; 107 while (fgets(buffer, sizeof(buffer), fp) != NULL) { 108 stats_line s; 109 int64_t rawTag; 110 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++; 152 } 153 if (i >= (int)limitIfaces.size()) { 154 // Nothing matched; skip this line. 155 //ALOGI("skipping due to iface: %s", buffer); 156 continue; 157 } 158 } 159 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 { 171 if (sscanf(pos, "%" PRIx64, &rawTag) != 1) { 172 ALOGE("bad tag: %s", pos); 173 fclose(fp); 174 return -1; 175 } 176 } 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; 183 184 // Ignore whitespace 185 while (*pos == ' ') pos++; 186 187 // Parse remaining fields. 188 if (sscanf(pos, "%u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, 189 &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 } 195 lines->push_back(s); 196 } else { 197 //ALOGI("skipping due to bad remaining fields: %s", pos); 198 } 199 } 200 201 if (fclose(fp) != 0) { 202 ALOGE("Failed to close netstats file"); 203 return -1; 204 } 205 return 0; 206} 207 208static int statsLinesToNetworkStats(JNIEnv* env, jclass clazz, jobject stats, 209 std::vector<stats_line>& lines) { 210 int size = lines.size(); 211 212 bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity); 213 214 ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats, 215 gNetworkStatsClassInfo.iface, size, grow)); 216 if (iface.get() == NULL) return -1; 217 ScopedIntArrayRW uid(env, get_int_array(env, stats, 218 gNetworkStatsClassInfo.uid, size, grow)); 219 if (uid.get() == NULL) return -1; 220 ScopedIntArrayRW set(env, get_int_array(env, stats, 221 gNetworkStatsClassInfo.set, size, grow)); 222 if (set.get() == NULL) return -1; 223 ScopedIntArrayRW tag(env, get_int_array(env, stats, 224 gNetworkStatsClassInfo.tag, size, grow)); 225 if (tag.get() == NULL) return -1; 226 ScopedIntArrayRW metered(env, get_int_array(env, stats, 227 gNetworkStatsClassInfo.metered, size, grow)); 228 if (metered.get() == NULL) return -1; 229 ScopedIntArrayRW roaming(env, get_int_array(env, stats, 230 gNetworkStatsClassInfo.roaming, size, grow)); 231 if (roaming.get() == NULL) return -1; 232 ScopedIntArrayRW defaultNetwork(env, get_int_array(env, stats, 233 gNetworkStatsClassInfo.defaultNetwork, size, grow)); 234 if (defaultNetwork.get() == NULL) return -1; 235 ScopedLongArrayRW rxBytes(env, get_long_array(env, stats, 236 gNetworkStatsClassInfo.rxBytes, size, grow)); 237 if (rxBytes.get() == NULL) return -1; 238 ScopedLongArrayRW rxPackets(env, get_long_array(env, stats, 239 gNetworkStatsClassInfo.rxPackets, size, grow)); 240 if (rxPackets.get() == NULL) return -1; 241 ScopedLongArrayRW txBytes(env, get_long_array(env, stats, 242 gNetworkStatsClassInfo.txBytes, size, grow)); 243 if (txBytes.get() == NULL) return -1; 244 ScopedLongArrayRW txPackets(env, get_long_array(env, stats, 245 gNetworkStatsClassInfo.txPackets, size, grow)); 246 if (txPackets.get() == NULL) return -1; 247 ScopedLongArrayRW operations(env, get_long_array(env, stats, 248 gNetworkStatsClassInfo.operations, size, grow)); 249 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; 258 // Metered, roaming and defaultNetwork are populated in Java-land. 259 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); 266 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()); 272 env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray()); 273 env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray()); 274 env->SetObjectField(stats, gNetworkStatsClassInfo.defaultNetwork, 275 defaultNetwork.getJavaArray()); 276 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 } 282 return 0; 283} 284 285static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path, 286 jint limitUid, jobjectArray limitIfacesObj, jint limitTag, 287 jboolean useBpfStats) { 288 ScopedUtfChars path8(env, path); 289 if (path8.c_str() == NULL) { 290 return -1; 291 } 292 293 std::vector<std::string> limitIfaces; 294 if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) { 295 int num = env->GetArrayLength(limitIfacesObj); 296 for (int i = 0; i < num; i++) { 297 jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i); 298 ScopedUtfChars string8(env, string); 299 if (string8.c_str() != NULL) { 300 limitIfaces.push_back(std::string(string8.c_str())); 301 } 302 } 303 } 304 std::vector<stats_line> lines; 305 306 307 if (useBpfStats) { 308 if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0) 309 return -1; 310 } else { 311 if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag, 312 limitUid, path8.c_str()) < 0) 313 return -1; 314 } 315 316 return statsLinesToNetworkStats(env, clazz, stats, lines); 317} 318 319static int readNetworkStatsDev(JNIEnv* env, jclass clazz, jobject stats) { 320 std::vector<stats_line> lines; 321 322 if (parseBpfNetworkStatsDev(&lines) < 0) 323 return -1; 324 325 return statsLinesToNetworkStats(env, clazz, stats, lines); 326} 327 328static const JNINativeMethod gMethods[] = { 329 { "nativeReadNetworkStatsDetail", 330 "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;IZ)I", 331 (void*) readNetworkStatsDetail }, 332 { "nativeReadNetworkStatsDev", "(Landroid/net/NetworkStats;)I", 333 (void*) readNetworkStatsDev }, 334}; 335 336int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) { 337 int err = RegisterMethodsOrDie(env, 338 "com/android/internal/net/NetworkStatsFactory", gMethods, 339 NELEM(gMethods)); 340 341 gStringClass = FindClassOrDie(env, "java/lang/String"); 342 gStringClass = MakeGlobalRefOrDie(env, gStringClass); 343 344 jclass clazz = FindClassOrDie(env, "android/net/NetworkStats"); 345 gNetworkStatsClassInfo.size = GetFieldIDOrDie(env, clazz, "size", "I"); 346 gNetworkStatsClassInfo.capacity = GetFieldIDOrDie(env, clazz, "capacity", "I"); 347 gNetworkStatsClassInfo.iface = GetFieldIDOrDie(env, clazz, "iface", "[Ljava/lang/String;"); 348 gNetworkStatsClassInfo.uid = GetFieldIDOrDie(env, clazz, "uid", "[I"); 349 gNetworkStatsClassInfo.set = GetFieldIDOrDie(env, clazz, "set", "[I"); 350 gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I"); 351 gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I"); 352 gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I"); 353 gNetworkStatsClassInfo.defaultNetwork = GetFieldIDOrDie(env, clazz, "defaultNetwork", "[I"); 354 gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J"); 355 gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J"); 356 gNetworkStatsClassInfo.txBytes = GetFieldIDOrDie(env, clazz, "txBytes", "[J"); 357 gNetworkStatsClassInfo.txPackets = GetFieldIDOrDie(env, clazz, "txPackets", "[J"); 358 gNetworkStatsClassInfo.operations = GetFieldIDOrDie(env, clazz, "operations", "[J"); 359 360 return err; 361} 362 363} 364