/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "NetworkStatsNative" #include #include #include #include #include #include #include "core_jni_helpers.h" #include #include #include #include #include "android-base/unique_fd.h" #include "bpf/BpfUtils.h" #include "netdbpf/BpfNetworkStats.h" using android::bpf::Stats; using android::bpf::bpfGetUidStats; using android::bpf::bpfGetIfaceStats; namespace android { static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt"; static const char* QTAGUID_UID_STATS = "/proc/net/xt_qtaguid/stats"; // NOTE: keep these in sync with TrafficStats.java static const uint64_t UNKNOWN = -1; enum StatsType { RX_BYTES = 0, RX_PACKETS = 1, TX_BYTES = 2, TX_PACKETS = 3, TCP_RX_PACKETS = 4, TCP_TX_PACKETS = 5 }; static uint64_t getStatsType(struct Stats* stats, StatsType type) { switch (type) { case RX_BYTES: return stats->rxBytes; case RX_PACKETS: return stats->rxPackets; case TX_BYTES: return stats->txBytes; case TX_PACKETS: return stats->txPackets; case TCP_RX_PACKETS: return stats->tcpRxPackets; case TCP_TX_PACKETS: return stats->tcpTxPackets; default: return UNKNOWN; } } static int parseIfaceStats(const char* iface, struct Stats* stats) { FILE *fp = fopen(QTAGUID_IFACE_STATS, "r"); if (fp == NULL) { return -1; } char buffer[384]; char cur_iface[32]; bool foundTcp = false; uint64_t rxBytes, rxPackets, txBytes, txPackets, tcpRxPackets, tcpTxPackets; while (fgets(buffer, sizeof(buffer), fp) != NULL) { int matched = sscanf(buffer, "%31s %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " " "%*u %" SCNu64 " %*u %*u %*u %*u " "%*u %" SCNu64 " %*u %*u %*u %*u", cur_iface, &rxBytes, &rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets); if (matched >= 5) { if (matched == 7) { foundTcp = true; } if (!iface || !strcmp(iface, cur_iface)) { stats->rxBytes += rxBytes; stats->rxPackets += rxPackets; stats->txBytes += txBytes; stats->txPackets += txPackets; if (matched == 7) { stats->tcpRxPackets += tcpRxPackets; stats->tcpTxPackets += tcpTxPackets; } } } } if (!foundTcp) { stats->tcpRxPackets = UNKNOWN; stats->tcpTxPackets = UNKNOWN; } if (fclose(fp) != 0) { return -1; } return 0; } static int parseUidStats(const uint32_t uid, struct Stats* stats) { FILE *fp = fopen(QTAGUID_UID_STATS, "r"); if (fp == NULL) { return -1; } char buffer[384]; char iface[32]; uint32_t idx, cur_uid, set; uint64_t tag, rxBytes, rxPackets, txBytes, txPackets; while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (sscanf(buffer, "%" SCNu32 " %31s 0x%" SCNx64 " %u %u %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 "", &idx, iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets, &txBytes, &txPackets) == 9) { if (uid == cur_uid && tag == 0L) { stats->rxBytes += rxBytes; stats->rxPackets += rxPackets; stats->txBytes += txBytes; stats->txPackets += txPackets; } } } if (fclose(fp) != 0) { return -1; } return 0; } static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) { struct Stats stats; memset(&stats, 0, sizeof(Stats)); if (useBpfStats) { if (bpfGetIfaceStats(NULL, &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } if (parseIfaceStats(NULL, &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type, jboolean useBpfStats) { ScopedUtfChars iface8(env, iface); if (iface8.c_str() == NULL) { return UNKNOWN; } struct Stats stats; memset(&stats, 0, sizeof(Stats)); if (useBpfStats) { if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } if (parseIfaceStats(iface8.c_str(), &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) { struct Stats stats; memset(&stats, 0, sizeof(Stats)); if (useBpfStats) { if (bpfGetUidStats(uid, &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } if (parseUidStats(uid, &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } static const JNINativeMethod gMethods[] = { {"nativeGetTotalStat", "(IZ)J", (void*) getTotalStat}, {"nativeGetIfaceStat", "(Ljava/lang/String;IZ)J", (void*) getIfaceStat}, {"nativeGetUidStat", "(IIZ)J", (void*) getUidStat}, }; int register_android_server_net_NetworkStatsService(JNIEnv* env) { jclass netStatsService = env->FindClass("com/android/server/net/NetworkStatsService"); jfieldID rxBytesId = env->GetStaticFieldID(netStatsService, "TYPE_RX_BYTES", "I"); jfieldID rxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_RX_PACKETS", "I"); jfieldID txBytesId = env->GetStaticFieldID(netStatsService, "TYPE_TX_BYTES", "I"); jfieldID txPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TX_PACKETS", "I"); jfieldID tcpRxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_RX_PACKETS", "I"); jfieldID tcpTxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_TX_PACKETS", "I"); env->SetStaticIntField(netStatsService, rxBytesId, RX_BYTES); env->SetStaticIntField(netStatsService, rxPacketsId, RX_PACKETS); env->SetStaticIntField(netStatsService, txBytesId, TX_BYTES); env->SetStaticIntField(netStatsService, txPacketsId, TX_PACKETS); env->SetStaticIntField(netStatsService, tcpRxPacketsId, TCP_RX_PACKETS); env->SetStaticIntField(netStatsService, tcpTxPacketsId, TCP_TX_PACKETS); return jniRegisterNativeMethods(env, "com/android/server/net/NetworkStatsService", gMethods, NELEM(gMethods)); } }