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