blob: acd56f2dfdad37e29747b618c3f594ae132f8d17 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.os.SystemClock;
import android.util.Slog;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
/**
* @hide
*/
public class SamplingDataTracker
{
private static final boolean DBG = false;
private static final String TAG = "SamplingDataTracker";
public static class SamplingSnapshot
{
public long mTxByteCount;
public long mRxByteCount;
public long mTxPacketCount;
public long mRxPacketCount;
public long mTxPacketErrorCount;
public long mRxPacketErrorCount;
public long mTimestamp;
}
public static void getSamplingSnapshots(Map<String, SamplingSnapshot> mapIfaceToSample) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("/proc/net/dev"));
// Skip over the line bearing column titles (there are 2 lines)
String line;
reader.readLine();
reader.readLine();
while ((line = reader.readLine()) != null) {
// remove leading whitespace
line = line.trim();
String[] tokens = line.split("[ ]+");
if (tokens.length < 17) {
continue;
}
/* column format is
* Interface (Recv)bytes packets errs drop fifo frame compressed multicast \
* (Transmit)bytes packets errs drop fifo colls carrier compress
*/
String currentIface = tokens[0].split(":")[0];
if (DBG) Slog.d(TAG, "Found data for interface " + currentIface);
if (mapIfaceToSample.containsKey(currentIface)) {
try {
SamplingSnapshot ss = new SamplingSnapshot();
ss.mTxByteCount = Long.parseLong(tokens[1]);
ss.mTxPacketCount = Long.parseLong(tokens[2]);
ss.mTxPacketErrorCount = Long.parseLong(tokens[3]);
ss.mRxByteCount = Long.parseLong(tokens[9]);
ss.mRxPacketCount = Long.parseLong(tokens[10]);
ss.mRxPacketErrorCount = Long.parseLong(tokens[11]);
ss.mTimestamp = SystemClock.elapsedRealtime();
if (DBG) {
Slog.d(TAG, "Interface = " + currentIface);
Slog.d(TAG, "ByteCount = " + String.valueOf(ss.mTxByteCount));
Slog.d(TAG, "TxPacketCount = " + String.valueOf(ss.mTxPacketCount));
Slog.d(TAG, "TxPacketErrorCount = "
+ String.valueOf(ss.mTxPacketErrorCount));
Slog.d(TAG, "RxByteCount = " + String.valueOf(ss.mRxByteCount));
Slog.d(TAG, "RxPacketCount = " + String.valueOf(ss.mRxPacketCount));
Slog.d(TAG, "RxPacketErrorCount = "
+ String.valueOf(ss.mRxPacketErrorCount));
Slog.d(TAG, "Timestamp = " + String.valueOf(ss.mTimestamp));
Slog.d(TAG, "---------------------------");
}
mapIfaceToSample.put(currentIface, ss);
} catch (NumberFormatException e) {
// just ignore this data point
}
}
}
if (DBG) {
Iterator it = mapIfaceToSample.entrySet().iterator();
while (it.hasNext()) {
Map.Entry kvpair = (Map.Entry)it.next();
if (kvpair.getValue() == null) {
Slog.d(TAG, "could not find snapshot for interface " + kvpair.getKey());
}
}
}
} catch(FileNotFoundException e) {
Slog.e(TAG, "could not find /proc/net/dev");
} catch (IOException e) {
Slog.e(TAG, "could not read /proc/net/dev");
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
Slog.e(TAG, "could not close /proc/net/dev");
}
}
}
// Snapshots from previous sampling interval
private SamplingSnapshot mBeginningSample;
private SamplingSnapshot mEndingSample;
// Starting snapshot of current interval
private SamplingSnapshot mLastSample;
// Protects sampling data from concurrent access
public final Object mSamplingDataLock = new Object();
// We need long enough time for a good sample
private final int MINIMUM_SAMPLING_INTERVAL = 15 * 1000;
// statistics is useless unless we have enough data
private final int MINIMUM_SAMPLED_PACKETS = 30;
public void startSampling(SamplingSnapshot s) {
synchronized(mSamplingDataLock) {
mLastSample = s;
}
}
public void stopSampling(SamplingSnapshot s) {
synchronized(mSamplingDataLock) {
if (mLastSample != null) {
if (s.mTimestamp - mLastSample.mTimestamp > MINIMUM_SAMPLING_INTERVAL
&& getSampledPacketCount(mLastSample, s) > MINIMUM_SAMPLED_PACKETS) {
mBeginningSample = mLastSample;
mEndingSample = s;
mLastSample = null;
} else {
if (DBG) Slog.d(TAG, "Throwing current sample away because it is too small");
}
}
}
}
public void resetSamplingData() {
if (DBG) Slog.d(TAG, "Resetting sampled network data");
synchronized(mSamplingDataLock) {
// We could just take another sample here and treat it as an
// 'ending sample' effectively shortening sampling interval, but that
// requires extra work (specifically, reading the sample needs to be
// done asynchronously)
mLastSample = null;
}
}
public long getSampledTxByteCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mTxByteCount - mBeginningSample.mTxByteCount;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
}
public long getSampledTxPacketCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mTxPacketCount - mBeginningSample.mTxPacketCount;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
}
public long getSampledTxPacketErrorCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mTxPacketErrorCount - mBeginningSample.mTxPacketErrorCount;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
}
public long getSampledRxByteCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mRxByteCount - mBeginningSample.mRxByteCount;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
}
public long getSampledRxPacketCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mRxPacketCount - mBeginningSample.mRxPacketCount;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
}
public long getSampledPacketCount() {
return getSampledPacketCount(mBeginningSample, mEndingSample);
}
public long getSampledPacketCount(SamplingSnapshot begin, SamplingSnapshot end) {
if (begin != null && end != null) {
long rxPacketCount = end.mRxPacketCount - begin.mRxPacketCount;
long txPacketCount = end.mTxPacketCount - begin.mTxPacketCount;
return rxPacketCount + txPacketCount;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
public long getSampledPacketErrorCount() {
if (mBeginningSample != null && mEndingSample != null) {
long rxPacketErrorCount = getSampledRxPacketErrorCount();
long txPacketErrorCount = getSampledTxPacketErrorCount();
return rxPacketErrorCount + txPacketErrorCount;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
public long getSampledRxPacketErrorCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mRxPacketErrorCount - mBeginningSample.mRxPacketErrorCount;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
}
public long getSampleTimestamp() {
synchronized(mSamplingDataLock) {
if (mEndingSample != null) {
return mEndingSample.mTimestamp;
} else {
return LinkQualityInfo.UNKNOWN_LONG;
}
}
}
public int getSampleDuration() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return (int) (mEndingSample.mTimestamp - mBeginningSample.mTimestamp);
} else {
return LinkQualityInfo.UNKNOWN_INT;
}
}
}
public void setCommonLinkQualityInfoFields(LinkQualityInfo li) {
synchronized(mSamplingDataLock) {
li.setLastDataSampleTime(getSampleTimestamp());
li.setDataSampleDuration(getSampleDuration());
li.setPacketCount(getSampledPacketCount());
li.setPacketErrorCount(getSampledPacketErrorCount());
}
}
}