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