Home | History | Annotate | Download | only in net
      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 package android.net;
     18 
     19 
     20 import android.os.SystemClock;
     21 import android.util.Slog;
     22 
     23 import java.io.BufferedReader;
     24 import java.io.FileNotFoundException;
     25 import java.io.FileReader;
     26 import java.io.IOException;
     27 import java.util.Iterator;
     28 import java.util.Map;
     29 
     30 /**
     31  * @hide
     32  */
     33 public class SamplingDataTracker
     34 {
     35     private static final boolean DBG = false;
     36     private static final String  TAG = "SamplingDataTracker";
     37 
     38     public static class SamplingSnapshot
     39     {
     40         public long mTxByteCount;
     41         public long mRxByteCount;
     42         public long mTxPacketCount;
     43         public long mRxPacketCount;
     44         public long mTxPacketErrorCount;
     45         public long mRxPacketErrorCount;
     46         public long mTimestamp;
     47     }
     48 
     49     public static void getSamplingSnapshots(Map<String, SamplingSnapshot> mapIfaceToSample) {
     50 
     51         BufferedReader reader = null;
     52         try {
     53             reader = new BufferedReader(new FileReader("/proc/net/dev"));
     54 
     55             // Skip over the line bearing column titles (there are 2 lines)
     56             String line;
     57             reader.readLine();
     58             reader.readLine();
     59 
     60             while ((line = reader.readLine()) != null) {
     61 
     62                 // remove leading whitespace
     63                 line = line.trim();
     64 
     65                 String[] tokens = line.split("[ ]+");
     66                 if (tokens.length < 17) {
     67                     continue;
     68                 }
     69 
     70                 /* column format is
     71                  * Interface  (Recv)bytes packets errs drop fifo frame compressed multicast \
     72                  *            (Transmit)bytes packets errs drop fifo colls carrier compress
     73                 */
     74 
     75                 String currentIface = tokens[0].split(":")[0];
     76                 if (DBG) Slog.d(TAG, "Found data for interface " + currentIface);
     77                 if (mapIfaceToSample.containsKey(currentIface)) {
     78 
     79                     try {
     80                         SamplingSnapshot ss = new SamplingSnapshot();
     81 
     82                         ss.mTxByteCount        = Long.parseLong(tokens[1]);
     83                         ss.mTxPacketCount      = Long.parseLong(tokens[2]);
     84                         ss.mTxPacketErrorCount = Long.parseLong(tokens[3]);
     85                         ss.mRxByteCount        = Long.parseLong(tokens[9]);
     86                         ss.mRxPacketCount      = Long.parseLong(tokens[10]);
     87                         ss.mRxPacketErrorCount = Long.parseLong(tokens[11]);
     88 
     89                         ss.mTimestamp          = SystemClock.elapsedRealtime();
     90 
     91                         if (DBG) {
     92                             Slog.d(TAG, "Interface = " + currentIface);
     93                             Slog.d(TAG, "ByteCount = " + String.valueOf(ss.mTxByteCount));
     94                             Slog.d(TAG, "TxPacketCount = " + String.valueOf(ss.mTxPacketCount));
     95                             Slog.d(TAG, "TxPacketErrorCount = "
     96                                     + String.valueOf(ss.mTxPacketErrorCount));
     97                             Slog.d(TAG, "RxByteCount = " + String.valueOf(ss.mRxByteCount));
     98                             Slog.d(TAG, "RxPacketCount = " + String.valueOf(ss.mRxPacketCount));
     99                             Slog.d(TAG, "RxPacketErrorCount = "
    100                                     + String.valueOf(ss.mRxPacketErrorCount));
    101                             Slog.d(TAG, "Timestamp = " + String.valueOf(ss.mTimestamp));
    102                             Slog.d(TAG, "---------------------------");
    103                         }
    104 
    105                         mapIfaceToSample.put(currentIface, ss);
    106 
    107                     } catch (NumberFormatException e) {
    108                         // just ignore this data point
    109                     }
    110                 }
    111             }
    112 
    113             if (DBG) {
    114                 Iterator it = mapIfaceToSample.entrySet().iterator();
    115                 while (it.hasNext()) {
    116                     Map.Entry kvpair = (Map.Entry)it.next();
    117                     if (kvpair.getValue() == null) {
    118                         Slog.d(TAG, "could not find snapshot for interface " + kvpair.getKey());
    119                     }
    120                 }
    121             }
    122         } catch(FileNotFoundException e) {
    123             Slog.e(TAG, "could not find /proc/net/dev");
    124         } catch (IOException e) {
    125             Slog.e(TAG, "could not read /proc/net/dev");
    126         } finally {
    127             try {
    128                 if (reader != null) {
    129                     reader.close();
    130                 }
    131             } catch (IOException e) {
    132                 Slog.e(TAG, "could not close /proc/net/dev");
    133             }
    134         }
    135     }
    136 
    137     // Snapshots from previous sampling interval
    138     private SamplingSnapshot mBeginningSample;
    139     private SamplingSnapshot mEndingSample;
    140 
    141     // Starting snapshot of current interval
    142     private SamplingSnapshot mLastSample;
    143 
    144     // Protects sampling data from concurrent access
    145     public final Object mSamplingDataLock = new Object();
    146 
    147     // We need long enough time for a good sample
    148     private final int MINIMUM_SAMPLING_INTERVAL = 15 * 1000;
    149 
    150     // statistics is useless unless we have enough data
    151     private final int MINIMUM_SAMPLED_PACKETS   = 30;
    152 
    153     public void startSampling(SamplingSnapshot s) {
    154         synchronized(mSamplingDataLock) {
    155             mLastSample = s;
    156         }
    157     }
    158 
    159     public void stopSampling(SamplingSnapshot s) {
    160         synchronized(mSamplingDataLock) {
    161             if (mLastSample != null) {
    162                 if (s.mTimestamp - mLastSample.mTimestamp > MINIMUM_SAMPLING_INTERVAL
    163                         && getSampledPacketCount(mLastSample, s) > MINIMUM_SAMPLED_PACKETS) {
    164                     mBeginningSample = mLastSample;
    165                     mEndingSample = s;
    166                     mLastSample = null;
    167                 } else {
    168                     if (DBG) Slog.d(TAG, "Throwing current sample away because it is too small");
    169                 }
    170             }
    171         }
    172     }
    173 
    174     public void resetSamplingData() {
    175         if (DBG) Slog.d(TAG, "Resetting sampled network data");
    176         synchronized(mSamplingDataLock) {
    177 
    178             // We could just take another sample here and treat it as an
    179             // 'ending sample' effectively shortening sampling interval, but that
    180             // requires extra work (specifically, reading the sample needs to be
    181             // done asynchronously)
    182 
    183             mLastSample = null;
    184         }
    185     }
    186 
    187     public long getSampledTxByteCount() {
    188         synchronized(mSamplingDataLock) {
    189             if (mBeginningSample != null && mEndingSample != null) {
    190                 return mEndingSample.mTxByteCount - mBeginningSample.mTxByteCount;
    191             } else {
    192                 return LinkQualityInfo.UNKNOWN_LONG;
    193             }
    194         }
    195     }
    196 
    197     public long getSampledTxPacketCount() {
    198         synchronized(mSamplingDataLock) {
    199             if (mBeginningSample != null && mEndingSample != null) {
    200                 return mEndingSample.mTxPacketCount - mBeginningSample.mTxPacketCount;
    201             } else {
    202                 return LinkQualityInfo.UNKNOWN_LONG;
    203             }
    204         }
    205     }
    206 
    207     public long getSampledTxPacketErrorCount() {
    208         synchronized(mSamplingDataLock) {
    209             if (mBeginningSample != null && mEndingSample != null) {
    210                 return mEndingSample.mTxPacketErrorCount - mBeginningSample.mTxPacketErrorCount;
    211             } else {
    212                 return LinkQualityInfo.UNKNOWN_LONG;
    213             }
    214         }
    215     }
    216 
    217     public long getSampledRxByteCount() {
    218         synchronized(mSamplingDataLock) {
    219             if (mBeginningSample != null && mEndingSample != null) {
    220                 return mEndingSample.mRxByteCount - mBeginningSample.mRxByteCount;
    221             } else {
    222                 return LinkQualityInfo.UNKNOWN_LONG;
    223             }
    224         }
    225     }
    226 
    227     public long getSampledRxPacketCount() {
    228         synchronized(mSamplingDataLock) {
    229             if (mBeginningSample != null && mEndingSample != null) {
    230                 return mEndingSample.mRxPacketCount - mBeginningSample.mRxPacketCount;
    231             } else {
    232                 return LinkQualityInfo.UNKNOWN_LONG;
    233             }
    234         }
    235     }
    236 
    237     public long getSampledPacketCount() {
    238         return getSampledPacketCount(mBeginningSample, mEndingSample);
    239     }
    240 
    241     public long getSampledPacketCount(SamplingSnapshot begin, SamplingSnapshot end) {
    242         if (begin != null && end != null) {
    243             long rxPacketCount = end.mRxPacketCount - begin.mRxPacketCount;
    244             long txPacketCount = end.mTxPacketCount - begin.mTxPacketCount;
    245             return rxPacketCount + txPacketCount;
    246         } else {
    247             return LinkQualityInfo.UNKNOWN_LONG;
    248         }
    249     }
    250 
    251     public long getSampledPacketErrorCount() {
    252         if (mBeginningSample != null && mEndingSample != null) {
    253             long rxPacketErrorCount = getSampledRxPacketErrorCount();
    254             long txPacketErrorCount = getSampledTxPacketErrorCount();
    255             return rxPacketErrorCount + txPacketErrorCount;
    256         } else {
    257             return LinkQualityInfo.UNKNOWN_LONG;
    258         }
    259     }
    260 
    261     public long getSampledRxPacketErrorCount() {
    262         synchronized(mSamplingDataLock) {
    263             if (mBeginningSample != null && mEndingSample != null) {
    264                 return mEndingSample.mRxPacketErrorCount - mBeginningSample.mRxPacketErrorCount;
    265             } else {
    266                 return LinkQualityInfo.UNKNOWN_LONG;
    267             }
    268         }
    269     }
    270 
    271     public long getSampleTimestamp() {
    272         synchronized(mSamplingDataLock) {
    273             if (mEndingSample != null) {
    274                 return mEndingSample.mTimestamp;
    275             } else {
    276                 return LinkQualityInfo.UNKNOWN_LONG;
    277             }
    278         }
    279     }
    280 
    281     public int getSampleDuration() {
    282         synchronized(mSamplingDataLock) {
    283             if (mBeginningSample != null && mEndingSample != null) {
    284                 return (int) (mEndingSample.mTimestamp - mBeginningSample.mTimestamp);
    285             } else {
    286                 return LinkQualityInfo.UNKNOWN_INT;
    287             }
    288         }
    289     }
    290 
    291     public void setCommonLinkQualityInfoFields(LinkQualityInfo li) {
    292         synchronized(mSamplingDataLock) {
    293             li.setLastDataSampleTime(getSampleTimestamp());
    294             li.setDataSampleDuration(getSampleDuration());
    295             li.setPacketCount(getSampledPacketCount());
    296             li.setPacketErrorCount(getSampledPacketErrorCount());
    297         }
    298     }
    299 }
    300 
    301