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 
getSamplingSnapshots(Map<String, SamplingSnapshot> mapIfaceToSample)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 
startSampling(SamplingSnapshot s)153     public void startSampling(SamplingSnapshot s) {
154         synchronized(mSamplingDataLock) {
155             mLastSample = s;
156         }
157     }
158 
stopSampling(SamplingSnapshot s)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 
resetSamplingData()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 
getSampledTxByteCount()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 
getSampledTxPacketCount()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 
getSampledTxPacketErrorCount()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 
getSampledRxByteCount()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 
getSampledRxPacketCount()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 
getSampledPacketCount()237     public long getSampledPacketCount() {
238         return getSampledPacketCount(mBeginningSample, mEndingSample);
239     }
240 
getSampledPacketCount(SamplingSnapshot begin, SamplingSnapshot end)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 
getSampledPacketErrorCount()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 
getSampledRxPacketErrorCount()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 
getSampleTimestamp()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 
getSampleDuration()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 
setCommonLinkQualityInfoFields(LinkQualityInfo li)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