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