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