Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2011 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 package com.android.internal.net;
     18 
     19 import static android.net.NetworkStats.SET_ALL;
     20 import static android.net.NetworkStats.TAG_NONE;
     21 import static android.net.NetworkStats.UID_ALL;
     22 import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
     23 
     24 import android.net.NetworkStats;
     25 import android.os.StrictMode;
     26 import android.os.SystemClock;
     27 
     28 import com.android.internal.annotations.VisibleForTesting;
     29 import com.android.internal.util.ProcFileReader;
     30 
     31 import java.io.File;
     32 import java.io.FileInputStream;
     33 import java.io.IOException;
     34 import java.net.ProtocolException;
     35 
     36 import libcore.io.IoUtils;
     37 
     38 /**
     39  * Creates {@link NetworkStats} instances by parsing various {@code /proc/}
     40  * files as needed.
     41  */
     42 public class NetworkStatsFactory {
     43     private static final String TAG = "NetworkStatsFactory";
     44 
     45     private static final boolean USE_NATIVE_PARSING = true;
     46     private static final boolean SANITY_CHECK_NATIVE = false;
     47 
     48     /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
     49     private final File mStatsXtIfaceAll;
     50     /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
     51     private final File mStatsXtIfaceFmt;
     52     /** Path to {@code /proc/net/xt_qtaguid/stats}. */
     53     private final File mStatsXtUid;
     54 
     55     public NetworkStatsFactory() {
     56         this(new File("/proc/"));
     57     }
     58 
     59     @VisibleForTesting
     60     public NetworkStatsFactory(File procRoot) {
     61         mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
     62         mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
     63         mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
     64     }
     65 
     66     /**
     67      * Parse and return interface-level summary {@link NetworkStats} measured
     68      * using {@code /proc/net/dev} style hooks, which may include non IP layer
     69      * traffic. Values monotonically increase since device boot, and may include
     70      * details about inactive interfaces.
     71      *
     72      * @throws IllegalStateException when problem parsing stats.
     73      */
     74     public NetworkStats readNetworkStatsSummaryDev() throws IOException {
     75         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
     76 
     77         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
     78         final NetworkStats.Entry entry = new NetworkStats.Entry();
     79 
     80         ProcFileReader reader = null;
     81         try {
     82             reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceAll));
     83 
     84             while (reader.hasMoreData()) {
     85                 entry.iface = reader.nextString();
     86                 entry.uid = UID_ALL;
     87                 entry.set = SET_ALL;
     88                 entry.tag = TAG_NONE;
     89 
     90                 final boolean active = reader.nextInt() != 0;
     91 
     92                 // always include snapshot values
     93                 entry.rxBytes = reader.nextLong();
     94                 entry.rxPackets = reader.nextLong();
     95                 entry.txBytes = reader.nextLong();
     96                 entry.txPackets = reader.nextLong();
     97 
     98                 // fold in active numbers, but only when active
     99                 if (active) {
    100                     entry.rxBytes += reader.nextLong();
    101                     entry.rxPackets += reader.nextLong();
    102                     entry.txBytes += reader.nextLong();
    103                     entry.txPackets += reader.nextLong();
    104                 }
    105 
    106                 stats.addValues(entry);
    107                 reader.finishLine();
    108             }
    109         } catch (NullPointerException e) {
    110             throw new ProtocolException("problem parsing stats", e);
    111         } catch (NumberFormatException e) {
    112             throw new ProtocolException("problem parsing stats", e);
    113         } finally {
    114             IoUtils.closeQuietly(reader);
    115             StrictMode.setThreadPolicy(savedPolicy);
    116         }
    117         return stats;
    118     }
    119 
    120     /**
    121      * Parse and return interface-level summary {@link NetworkStats}. Designed
    122      * to return only IP layer traffic. Values monotonically increase since
    123      * device boot, and may include details about inactive interfaces.
    124      *
    125      * @throws IllegalStateException when problem parsing stats.
    126      */
    127     public NetworkStats readNetworkStatsSummaryXt() throws IOException {
    128         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
    129 
    130         // return null when kernel doesn't support
    131         if (!mStatsXtIfaceFmt.exists()) return null;
    132 
    133         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
    134         final NetworkStats.Entry entry = new NetworkStats.Entry();
    135 
    136         ProcFileReader reader = null;
    137         try {
    138             // open and consume header line
    139             reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt));
    140             reader.finishLine();
    141 
    142             while (reader.hasMoreData()) {
    143                 entry.iface = reader.nextString();
    144                 entry.uid = UID_ALL;
    145                 entry.set = SET_ALL;
    146                 entry.tag = TAG_NONE;
    147 
    148                 entry.rxBytes = reader.nextLong();
    149                 entry.rxPackets = reader.nextLong();
    150                 entry.txBytes = reader.nextLong();
    151                 entry.txPackets = reader.nextLong();
    152 
    153                 stats.addValues(entry);
    154                 reader.finishLine();
    155             }
    156         } catch (NullPointerException e) {
    157             throw new ProtocolException("problem parsing stats", e);
    158         } catch (NumberFormatException e) {
    159             throw new ProtocolException("problem parsing stats", e);
    160         } finally {
    161             IoUtils.closeQuietly(reader);
    162             StrictMode.setThreadPolicy(savedPolicy);
    163         }
    164         return stats;
    165     }
    166 
    167     public NetworkStats readNetworkStatsDetail() throws IOException {
    168         return readNetworkStatsDetail(UID_ALL);
    169     }
    170 
    171     public NetworkStats readNetworkStatsDetail(int limitUid) throws IOException {
    172         if (USE_NATIVE_PARSING) {
    173             final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
    174             if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid) != 0) {
    175                 throw new IOException("Failed to parse network stats");
    176             }
    177             if (SANITY_CHECK_NATIVE) {
    178                 final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
    179                 assertEquals(javaStats, stats);
    180             }
    181             return stats;
    182         } else {
    183             return javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
    184         }
    185     }
    186 
    187     /**
    188      * Parse and return {@link NetworkStats} with UID-level details. Values are
    189      * expected to monotonically increase since device boot.
    190      */
    191     @VisibleForTesting
    192     public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid)
    193             throws IOException {
    194         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
    195 
    196         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
    197         final NetworkStats.Entry entry = new NetworkStats.Entry();
    198 
    199         int idx = 1;
    200         int lastIdx = 1;
    201 
    202         ProcFileReader reader = null;
    203         try {
    204             // open and consume header line
    205             reader = new ProcFileReader(new FileInputStream(detailPath));
    206             reader.finishLine();
    207 
    208             while (reader.hasMoreData()) {
    209                 idx = reader.nextInt();
    210                 if (idx != lastIdx + 1) {
    211                     throw new ProtocolException(
    212                             "inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
    213                 }
    214                 lastIdx = idx;
    215 
    216                 entry.iface = reader.nextString();
    217                 entry.tag = kernelToTag(reader.nextString());
    218                 entry.uid = reader.nextInt();
    219                 entry.set = reader.nextInt();
    220                 entry.rxBytes = reader.nextLong();
    221                 entry.rxPackets = reader.nextLong();
    222                 entry.txBytes = reader.nextLong();
    223                 entry.txPackets = reader.nextLong();
    224 
    225                 if (limitUid == UID_ALL || limitUid == entry.uid) {
    226                     stats.addValues(entry);
    227                 }
    228 
    229                 reader.finishLine();
    230             }
    231         } catch (NullPointerException e) {
    232             throw new ProtocolException("problem parsing idx " + idx, e);
    233         } catch (NumberFormatException e) {
    234             throw new ProtocolException("problem parsing idx " + idx, e);
    235         } finally {
    236             IoUtils.closeQuietly(reader);
    237             StrictMode.setThreadPolicy(savedPolicy);
    238         }
    239 
    240         return stats;
    241     }
    242 
    243     public void assertEquals(NetworkStats expected, NetworkStats actual) {
    244         if (expected.size() != actual.size()) {
    245             throw new AssertionError(
    246                     "Expected size " + expected.size() + ", actual size " + actual.size());
    247         }
    248 
    249         NetworkStats.Entry expectedRow = null;
    250         NetworkStats.Entry actualRow = null;
    251         for (int i = 0; i < expected.size(); i++) {
    252             expectedRow = expected.getValues(i, expectedRow);
    253             actualRow = actual.getValues(i, actualRow);
    254             if (!expectedRow.equals(actualRow)) {
    255                 throw new AssertionError(
    256                         "Expected row " + i + ": " + expectedRow + ", actual row " + actualRow);
    257             }
    258         }
    259     }
    260 
    261     /**
    262      * Parse statistics from file into given {@link NetworkStats} object. Values
    263      * are expected to monotonically increase since device boot.
    264      */
    265     @VisibleForTesting
    266     public static native int nativeReadNetworkStatsDetail(
    267             NetworkStats stats, String path, int limitUid);
    268 }
    269