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 #include <utils/Vector.h> 34 35 namespace android { 36 37 static jclass gStringClass; 38 39 static struct { 40 jfieldID size; 41 jfieldID capacity; 42 jfieldID iface; 43 jfieldID uid; 44 jfieldID set; 45 jfieldID tag; 46 jfieldID metered; 47 jfieldID roaming; 48 jfieldID rxBytes; 49 jfieldID rxPackets; 50 jfieldID txBytes; 51 jfieldID txPackets; 52 jfieldID operations; 53 } gNetworkStatsClassInfo; 54 55 struct stats_line { 56 char iface[32]; 57 int32_t uid; 58 int32_t set; 59 int32_t tag; 60 int64_t rxBytes; 61 int64_t rxPackets; 62 int64_t txBytes; 63 int64_t txPackets; 64 }; 65 66 static jobjectArray get_string_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow) 67 { 68 if (!grow) { 69 jobjectArray array = (jobjectArray)env->GetObjectField(obj, field); 70 if (array != NULL) { 71 return array; 72 } 73 } 74 return env->NewObjectArray(size, gStringClass, NULL); 75 } 76 77 static jintArray get_int_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow) 78 { 79 if (!grow) { 80 jintArray array = (jintArray)env->GetObjectField(obj, field); 81 if (array != NULL) { 82 return array; 83 } 84 } 85 return env->NewIntArray(size); 86 } 87 88 static jlongArray get_long_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow) 89 { 90 if (!grow) { 91 jlongArray array = (jlongArray)env->GetObjectField(obj, field); 92 if (array != NULL) { 93 return array; 94 } 95 } 96 return env->NewLongArray(size); 97 } 98 99 static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, 100 jstring path, jint limitUid, jobjectArray limitIfacesObj, jint limitTag) { 101 ScopedUtfChars path8(env, path); 102 if (path8.c_str() == NULL) { 103 return -1; 104 } 105 106 FILE *fp = fopen(path8.c_str(), "r"); 107 if (fp == NULL) { 108 return -1; 109 } 110 111 Vector<String8> limitIfaces; 112 if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) { 113 int num = env->GetArrayLength(limitIfacesObj); 114 limitIfaces.setCapacity(num); 115 for (int i=0; i<num; i++) { 116 jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i); 117 ScopedUtfChars string8(env, string); 118 if (string8.c_str() != NULL) { 119 limitIfaces.add(String8(string8.c_str())); 120 } 121 } 122 } 123 124 Vector<stats_line> lines; 125 126 int lastIdx = 1; 127 int idx; 128 char buffer[384]; 129 while (fgets(buffer, sizeof(buffer), fp) != NULL) { 130 stats_line s; 131 int64_t rawTag; 132 char* pos = buffer; 133 char* endPos; 134 // First field is the index. 135 idx = (int)strtol(pos, &endPos, 10); 136 //ALOGI("Index #%d: %s", idx, buffer); 137 if (pos == endPos) { 138 // Skip lines that don't start with in index. In particular, 139 // this will skip the initial header line. 140 continue; 141 } 142 if (idx != lastIdx + 1) { 143 ALOGE("inconsistent idx=%d after lastIdx=%d: %s", idx, lastIdx, buffer); 144 fclose(fp); 145 return -1; 146 } 147 lastIdx = idx; 148 pos = endPos; 149 // Skip whitespace. 150 while (*pos == ' ') { 151 pos++; 152 } 153 // Next field is iface. 154 int ifaceIdx = 0; 155 while (*pos != ' ' && *pos != 0 && ifaceIdx < (int)(sizeof(s.iface)-1)) { 156 s.iface[ifaceIdx] = *pos; 157 ifaceIdx++; 158 pos++; 159 } 160 if (*pos != ' ') { 161 ALOGE("bad iface: %s", buffer); 162 fclose(fp); 163 return -1; 164 } 165 s.iface[ifaceIdx] = 0; 166 if (limitIfaces.size() > 0) { 167 // Is this an iface the caller is interested in? 168 int i = 0; 169 while (i < (int)limitIfaces.size()) { 170 if (limitIfaces[i] == s.iface) { 171 break; 172 } 173 i++; 174 } 175 if (i >= (int)limitIfaces.size()) { 176 // Nothing matched; skip this line. 177 //ALOGI("skipping due to iface: %s", buffer); 178 continue; 179 } 180 } 181 182 // Ignore whitespace 183 while (*pos == ' ') pos++; 184 185 // Find end of tag field 186 endPos = pos; 187 while (*endPos != ' ') endPos++; 188 189 // Three digit field is always 0x0, otherwise parse 190 if (endPos - pos == 3) { 191 rawTag = 0; 192 } else { 193 if (sscanf(pos, "%" PRIx64, &rawTag) != 1) { 194 ALOGE("bad tag: %s", pos); 195 fclose(fp); 196 return -1; 197 } 198 } 199 s.tag = rawTag >> 32; 200 if (limitTag != -1 && s.tag != limitTag) { 201 //ALOGI("skipping due to tag: %s", buffer); 202 continue; 203 } 204 pos = endPos; 205 206 // Ignore whitespace 207 while (*pos == ' ') pos++; 208 209 // Parse remaining fields. 210 if (sscanf(pos, "%u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, 211 &s.uid, &s.set, &s.rxBytes, &s.rxPackets, 212 &s.txBytes, &s.txPackets) == 6) { 213 if (limitUid != -1 && limitUid != s.uid) { 214 //ALOGI("skipping due to uid: %s", buffer); 215 continue; 216 } 217 lines.push_back(s); 218 } else { 219 //ALOGI("skipping due to bad remaining fields: %s", pos); 220 } 221 } 222 223 if (fclose(fp) != 0) { 224 ALOGE("Failed to close netstats file"); 225 return -1; 226 } 227 228 int size = lines.size(); 229 bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity); 230 231 ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats, 232 gNetworkStatsClassInfo.iface, size, grow)); 233 if (iface.get() == NULL) return -1; 234 ScopedIntArrayRW uid(env, get_int_array(env, stats, 235 gNetworkStatsClassInfo.uid, size, grow)); 236 if (uid.get() == NULL) return -1; 237 ScopedIntArrayRW set(env, get_int_array(env, stats, 238 gNetworkStatsClassInfo.set, size, grow)); 239 if (set.get() == NULL) return -1; 240 ScopedIntArrayRW tag(env, get_int_array(env, stats, 241 gNetworkStatsClassInfo.tag, size, grow)); 242 if (tag.get() == NULL) return -1; 243 ScopedIntArrayRW metered(env, get_int_array(env, stats, 244 gNetworkStatsClassInfo.metered, size, grow)); 245 if (metered.get() == NULL) return -1; 246 ScopedIntArrayRW roaming(env, get_int_array(env, stats, 247 gNetworkStatsClassInfo.roaming, size, grow)); 248 if (roaming.get() == NULL) return -1; 249 ScopedLongArrayRW rxBytes(env, get_long_array(env, stats, 250 gNetworkStatsClassInfo.rxBytes, size, grow)); 251 if (rxBytes.get() == NULL) return -1; 252 ScopedLongArrayRW rxPackets(env, get_long_array(env, stats, 253 gNetworkStatsClassInfo.rxPackets, size, grow)); 254 if (rxPackets.get() == NULL) return -1; 255 ScopedLongArrayRW txBytes(env, get_long_array(env, stats, 256 gNetworkStatsClassInfo.txBytes, size, grow)); 257 if (txBytes.get() == NULL) return -1; 258 ScopedLongArrayRW txPackets(env, get_long_array(env, stats, 259 gNetworkStatsClassInfo.txPackets, size, grow)); 260 if (txPackets.get() == NULL) return -1; 261 ScopedLongArrayRW operations(env, get_long_array(env, stats, 262 gNetworkStatsClassInfo.operations, size, grow)); 263 if (operations.get() == NULL) return -1; 264 265 for (int i = 0; i < size; i++) { 266 ScopedLocalRef<jstring> ifaceString(env, env->NewStringUTF(lines[i].iface)); 267 env->SetObjectArrayElement(iface.get(), i, ifaceString.get()); 268 269 uid[i] = lines[i].uid; 270 set[i] = lines[i].set; 271 tag[i] = lines[i].tag; 272 // Metered and Roaming are populated in Java-land by inspecting the iface properties. 273 rxBytes[i] = lines[i].rxBytes; 274 rxPackets[i] = lines[i].rxPackets; 275 txBytes[i] = lines[i].txBytes; 276 txPackets[i] = lines[i].txPackets; 277 } 278 279 env->SetIntField(stats, gNetworkStatsClassInfo.size, size); 280 if (grow) { 281 env->SetIntField(stats, gNetworkStatsClassInfo.capacity, size); 282 env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get()); 283 env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray()); 284 env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray()); 285 env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray()); 286 env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray()); 287 env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray()); 288 env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray()); 289 env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray()); 290 env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray()); 291 env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray()); 292 env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray()); 293 } 294 295 return 0; 296 } 297 298 static const JNINativeMethod gMethods[] = { 299 { "nativeReadNetworkStatsDetail", 300 "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I", 301 (void*) readNetworkStatsDetail } 302 }; 303 304 int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) { 305 int err = RegisterMethodsOrDie(env, 306 "com/android/internal/net/NetworkStatsFactory", gMethods, 307 NELEM(gMethods)); 308 309 gStringClass = FindClassOrDie(env, "java/lang/String"); 310 gStringClass = MakeGlobalRefOrDie(env, gStringClass); 311 312 jclass clazz = FindClassOrDie(env, "android/net/NetworkStats"); 313 gNetworkStatsClassInfo.size = GetFieldIDOrDie(env, clazz, "size", "I"); 314 gNetworkStatsClassInfo.capacity = GetFieldIDOrDie(env, clazz, "capacity", "I"); 315 gNetworkStatsClassInfo.iface = GetFieldIDOrDie(env, clazz, "iface", "[Ljava/lang/String;"); 316 gNetworkStatsClassInfo.uid = GetFieldIDOrDie(env, clazz, "uid", "[I"); 317 gNetworkStatsClassInfo.set = GetFieldIDOrDie(env, clazz, "set", "[I"); 318 gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I"); 319 gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I"); 320 gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I"); 321 gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J"); 322 gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J"); 323 gNetworkStatsClassInfo.txBytes = GetFieldIDOrDie(env, clazz, "txBytes", "[J"); 324 gNetworkStatsClassInfo.txPackets = GetFieldIDOrDie(env, clazz, "txPackets", "[J"); 325 gNetworkStatsClassInfo.operations = GetFieldIDOrDie(env, clazz, "operations", "[J"); 326 327 return err; 328 } 329 330 } 331