com_android_internal_net_NetworkStatsFactory.cpp revision d0c5b9abed60b7bc056d026bf0f2b2235410fb70
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 34namespace android { 35 36static jclass gStringClass; 37 38static 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 52struct 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 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 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 // Skip whitespace. 179 while (*pos == ' ') { 180 pos++; 181 } 182 // Next field is tag. 183 rawTag = strtoll(pos, &endPos, 16); 184 //ALOGI("Index #%d: %s", idx, buffer); 185 if (pos == endPos) { 186 ALOGE("bad tag: %s", pos); 187 fclose(fp); 188 return -1; 189 } 190 s.tag = rawTag >> 32; 191 if (limitTag != -1 && s.tag != limitTag) { 192 //ALOGI("skipping due to tag: %s", buffer); 193 continue; 194 } 195 pos = endPos; 196 // Skip whitespace. 197 while (*pos == ' ') { 198 pos++; 199 } 200 // Parse remaining fields. 201 if (sscanf(pos, "%u %u %llu %llu %llu %llu", 202 &s.uid, &s.set, &s.rxBytes, &s.rxPackets, 203 &s.txBytes, &s.txPackets) == 6) { 204 if (limitUid != -1 && limitUid != s.uid) { 205 //ALOGI("skipping due to uid: %s", buffer); 206 continue; 207 } 208 lines.push_back(s); 209 } else { 210 //ALOGI("skipping due to bad remaining fields: %s", pos); 211 } 212 } 213 214 if (fclose(fp) != 0) { 215 ALOGE("Failed to close netstats file"); 216 return -1; 217 } 218 219 int size = lines.size(); 220 bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity); 221 222 ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats, 223 gNetworkStatsClassInfo.iface, size, grow)); 224 if (iface.get() == NULL) return -1; 225 ScopedIntArrayRW uid(env, get_int_array(env, stats, 226 gNetworkStatsClassInfo.uid, size, grow)); 227 if (uid.get() == NULL) return -1; 228 ScopedIntArrayRW set(env, get_int_array(env, stats, 229 gNetworkStatsClassInfo.set, size, grow)); 230 if (set.get() == NULL) return -1; 231 ScopedIntArrayRW tag(env, get_int_array(env, stats, 232 gNetworkStatsClassInfo.tag, size, grow)); 233 if (tag.get() == NULL) return -1; 234 ScopedLongArrayRW rxBytes(env, get_long_array(env, stats, 235 gNetworkStatsClassInfo.rxBytes, size, grow)); 236 if (rxBytes.get() == NULL) return -1; 237 ScopedLongArrayRW rxPackets(env, get_long_array(env, stats, 238 gNetworkStatsClassInfo.rxPackets, size, grow)); 239 if (rxPackets.get() == NULL) return -1; 240 ScopedLongArrayRW txBytes(env, get_long_array(env, stats, 241 gNetworkStatsClassInfo.txBytes, size, grow)); 242 if (txBytes.get() == NULL) return -1; 243 ScopedLongArrayRW txPackets(env, get_long_array(env, stats, 244 gNetworkStatsClassInfo.txPackets, size, grow)); 245 if (txPackets.get() == NULL) return -1; 246 ScopedLongArrayRW operations(env, get_long_array(env, stats, 247 gNetworkStatsClassInfo.operations, size, grow)); 248 if (operations.get() == NULL) return -1; 249 250 for (int i = 0; i < size; i++) { 251 ScopedLocalRef<jstring> ifaceString(env, env->NewStringUTF(lines[i].iface)); 252 env->SetObjectArrayElement(iface.get(), i, ifaceString.get()); 253 254 uid[i] = lines[i].uid; 255 set[i] = lines[i].set; 256 tag[i] = lines[i].tag; 257 rxBytes[i] = lines[i].rxBytes; 258 rxPackets[i] = lines[i].rxPackets; 259 txBytes[i] = lines[i].txBytes; 260 txPackets[i] = lines[i].txPackets; 261 } 262 263 env->SetIntField(stats, gNetworkStatsClassInfo.size, size); 264 if (grow) { 265 env->SetIntField(stats, gNetworkStatsClassInfo.capacity, size); 266 env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get()); 267 env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray()); 268 env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray()); 269 env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray()); 270 env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray()); 271 env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray()); 272 env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray()); 273 env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray()); 274 env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray()); 275 } 276 277 return 0; 278} 279 280static jclass findClass(JNIEnv* env, const char* name) { 281 ScopedLocalRef<jclass> localClass(env, env->FindClass(name)); 282 jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); 283 if (result == NULL) { 284 ALOGE("failed to find class '%s'", name); 285 abort(); 286 } 287 return result; 288} 289 290static JNINativeMethod gMethods[] = { 291 { "nativeReadNetworkStatsDetail", 292 "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I", 293 (void*) readNetworkStatsDetail } 294}; 295 296int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) { 297 int err = AndroidRuntime::registerNativeMethods(env, 298 "com/android/internal/net/NetworkStatsFactory", gMethods, 299 NELEM(gMethods)); 300 301 gStringClass = findClass(env, "java/lang/String"); 302 303 jclass clazz = env->FindClass("android/net/NetworkStats"); 304 gNetworkStatsClassInfo.size = env->GetFieldID(clazz, "size", "I"); 305 gNetworkStatsClassInfo.capacity = env->GetFieldID(clazz, "capacity", "I"); 306 gNetworkStatsClassInfo.iface = env->GetFieldID(clazz, "iface", "[Ljava/lang/String;"); 307 gNetworkStatsClassInfo.uid = env->GetFieldID(clazz, "uid", "[I"); 308 gNetworkStatsClassInfo.set = env->GetFieldID(clazz, "set", "[I"); 309 gNetworkStatsClassInfo.tag = env->GetFieldID(clazz, "tag", "[I"); 310 gNetworkStatsClassInfo.rxBytes = env->GetFieldID(clazz, "rxBytes", "[J"); 311 gNetworkStatsClassInfo.rxPackets = env->GetFieldID(clazz, "rxPackets", "[J"); 312 gNetworkStatsClassInfo.txBytes = env->GetFieldID(clazz, "txBytes", "[J"); 313 gNetworkStatsClassInfo.txPackets = env->GetFieldID(clazz, "txPackets", "[J"); 314 gNetworkStatsClassInfo.operations = env->GetFieldID(clazz, "operations", "[J"); 315 316 return err; 317} 318 319} 320