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