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
17package android.net;
18
19
20import android.os.SystemClock;
21import android.util.Slog;
22
23import java.io.BufferedReader;
24import java.io.FileNotFoundException;
25import java.io.FileReader;
26import java.io.IOException;
27import java.util.Iterator;
28import java.util.Map;
29
30/**
31 * @hide
32 */
33public 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