diff options
Diffstat (limited to 'ddms/libs/ddmuilib/src/com/android/ddmuilib/net/NetworkPanel.java')
-rw-r--r-- | ddms/libs/ddmuilib/src/com/android/ddmuilib/net/NetworkPanel.java | 1125 |
1 files changed, 0 insertions, 1125 deletions
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/net/NetworkPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/net/NetworkPanel.java deleted file mode 100644 index 15b8b56..0000000 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/net/NetworkPanel.java +++ /dev/null @@ -1,1125 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.ddmuilib.net; - -import com.android.ddmlib.AdbCommandRejectedException; -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.MultiLineReceiver; -import com.android.ddmlib.ShellCommandUnresponsiveException; -import com.android.ddmlib.TimeoutException; -import com.android.ddmuilib.DdmUiPreferences; -import com.android.ddmuilib.TableHelper; -import com.android.ddmuilib.TablePanel; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.jface.dialogs.ErrorDialog; -import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.FormAttachment; -import org.eclipse.swt.layout.FormData; -import org.eclipse.swt.layout.FormLayout; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.jfree.chart.ChartFactory; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.axis.AxisLocation; -import org.jfree.chart.axis.NumberAxis; -import org.jfree.chart.axis.ValueAxis; -import org.jfree.chart.plot.DatasetRenderingOrder; -import org.jfree.chart.plot.ValueMarker; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.StackedXYAreaRenderer2; -import org.jfree.chart.renderer.xy.XYAreaRenderer; -import org.jfree.data.DefaultKeyedValues2D; -import org.jfree.data.time.Millisecond; -import org.jfree.data.time.TimePeriod; -import org.jfree.data.time.TimeSeries; -import org.jfree.data.time.TimeSeriesCollection; -import org.jfree.data.xy.AbstractIntervalXYDataset; -import org.jfree.data.xy.TableXYDataset; -import org.jfree.experimental.chart.swt.ChartComposite; -import org.jfree.ui.RectangleAnchor; -import org.jfree.ui.TextAnchor; - -import java.io.IOException; -import java.text.DecimalFormat; -import java.text.FieldPosition; -import java.text.NumberFormat; -import java.text.ParsePosition; -import java.util.ArrayList; -import java.util.Date; -import java.util.Formatter; -import java.util.Iterator; - -/** - * Displays live network statistics for currently selected {@link Client}. - */ -public class NetworkPanel extends TablePanel { - - // TODO: enable view of packets and bytes/packet - // TODO: add sash to resize chart and table - // TODO: let user edit tags to be meaningful - - /** Amount of historical data to display. */ - private static final long HISTORY_MILLIS = 30 * 1000; - - private final static String PREFS_NETWORK_COL_TITLE = "networkPanel.title"; - private final static String PREFS_NETWORK_COL_RX_BYTES = "networkPanel.rxBytes"; - private final static String PREFS_NETWORK_COL_RX_PACKETS = "networkPanel.rxPackets"; - private final static String PREFS_NETWORK_COL_TX_BYTES = "networkPanel.txBytes"; - private final static String PREFS_NETWORK_COL_TX_PACKETS = "networkPanel.txPackets"; - - /** Path to network statistics on remote device. */ - private static final String PROC_XT_QTAGUID = "/proc/net/xt_qtaguid/stats"; - - private static final java.awt.Color TOTAL_COLOR = java.awt.Color.GRAY; - - /** Colors used for tag series data. */ - private static final java.awt.Color[] SERIES_COLORS = new java.awt.Color[] { - java.awt.Color.decode("0x2bc4c1"), // teal - java.awt.Color.decode("0xD50F25"), // red - java.awt.Color.decode("0x3369E8"), // blue - java.awt.Color.decode("0xEEB211"), // orange - java.awt.Color.decode("0x00bd2e"), // green - java.awt.Color.decode("0xae26ae"), // purple - }; - - private Display mDisplay; - - private Composite mPanel; - - /** Header panel with configuration options. */ - private Composite mHeader; - - private Label mSpeedLabel; - private Combo mSpeedCombo; - - /** Current sleep between each sample, from {@link #mSpeedCombo}. */ - private long mSpeedMillis; - - private Button mRunningButton; - private Button mResetButton; - - /** Chart of recent network activity. */ - private JFreeChart mChart; - private ChartComposite mChartComposite; - - private ValueAxis mDomainAxis; - - /** Data for total traffic (tag 0x0). */ - private TimeSeriesCollection mTotalCollection; - private TimeSeries mRxTotalSeries; - private TimeSeries mTxTotalSeries; - - /** Data for detailed tagged traffic. */ - private LiveTimeTableXYDataset mRxDetailDataset; - private LiveTimeTableXYDataset mTxDetailDataset; - - private XYAreaRenderer mTotalRenderer; - private StackedXYAreaRenderer2 mRenderer; - - /** Table showing summary of network activity. */ - private Table mTable; - private TableViewer mTableViewer; - - /** UID of currently selected {@link Client}. */ - private int mActiveUid = -1; - - /** List of traffic flows being actively tracked. */ - private ArrayList<TrackedItem> mTrackedItems = new ArrayList<TrackedItem>(); - - private SampleThread mSampleThread; - - private class SampleThread extends Thread { - private volatile boolean mFinish; - - public void finish() { - mFinish = true; - interrupt(); - } - - @Override - public void run() { - while (!mFinish && !mDisplay.isDisposed()) { - performSample(); - - try { - Thread.sleep(mSpeedMillis); - } catch (InterruptedException e) { - // ignored - } - } - } - } - - /** Last snapshot taken by {@link #performSample()}. */ - private NetworkSnapshot mLastSnapshot; - - @Override - protected Control createControl(Composite parent) { - mDisplay = parent.getDisplay(); - - mPanel = new Composite(parent, SWT.NONE); - - final FormLayout formLayout = new FormLayout(); - mPanel.setLayout(formLayout); - - createHeader(); - createChart(); - createTable(); - - return mPanel; - } - - /** - * Create header panel with configuration options. - */ - private void createHeader() { - - mHeader = new Composite(mPanel, SWT.NONE); - final RowLayout layout = new RowLayout(); - layout.center = true; - mHeader.setLayout(layout); - - mSpeedLabel = new Label(mHeader, SWT.NONE); - mSpeedLabel.setText("Speed:"); - mSpeedCombo = new Combo(mHeader, SWT.PUSH); - mSpeedCombo.add("Fast (100ms)"); - mSpeedCombo.add("Medium (250ms)"); - mSpeedCombo.add("Slow (500ms)"); - mSpeedCombo.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - updateSpeed(); - } - }); - - mSpeedCombo.select(1); - updateSpeed(); - - mRunningButton = new Button(mHeader, SWT.PUSH); - mRunningButton.setText("Start"); - mRunningButton.setEnabled(false); - mRunningButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - final boolean alreadyRunning = mSampleThread != null; - updateRunning(!alreadyRunning); - } - }); - - mResetButton = new Button(mHeader, SWT.PUSH); - mResetButton.setText("Reset"); - mResetButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - clearTrackedItems(); - } - }); - - final FormData data = new FormData(); - data.top = new FormAttachment(0); - data.left = new FormAttachment(0); - data.right = new FormAttachment(100); - mHeader.setLayoutData(data); - } - - /** - * Create chart of recent network activity. - */ - private void createChart() { - - mChart = ChartFactory.createTimeSeriesChart(null, null, null, null, false, false, false); - - // create backing datasets and series - mRxTotalSeries = new TimeSeries("RX total"); - mTxTotalSeries = new TimeSeries("TX total"); - - mRxTotalSeries.setMaximumItemAge(HISTORY_MILLIS); - mTxTotalSeries.setMaximumItemAge(HISTORY_MILLIS); - - mTotalCollection = new TimeSeriesCollection(); - mTotalCollection.addSeries(mRxTotalSeries); - mTotalCollection.addSeries(mTxTotalSeries); - - mRxDetailDataset = new LiveTimeTableXYDataset(); - mTxDetailDataset = new LiveTimeTableXYDataset(); - - mTotalRenderer = new XYAreaRenderer(XYAreaRenderer.AREA); - mRenderer = new StackedXYAreaRenderer2(); - - final XYPlot xyPlot = mChart.getXYPlot(); - - xyPlot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD); - - xyPlot.setDataset(0, mTotalCollection); - xyPlot.setDataset(1, mRxDetailDataset); - xyPlot.setDataset(2, mTxDetailDataset); - xyPlot.setRenderer(0, mTotalRenderer); - xyPlot.setRenderer(1, mRenderer); - xyPlot.setRenderer(2, mRenderer); - - // we control domain axis manually when taking samples - mDomainAxis = xyPlot.getDomainAxis(); - mDomainAxis.setAutoRange(false); - - final NumberAxis axis = new NumberAxis(); - axis.setNumberFormatOverride(new BytesFormat(true)); - axis.setAutoRangeMinimumSize(50); - xyPlot.setRangeAxis(axis); - xyPlot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); - - // draw thick line to separate RX versus TX traffic - xyPlot.addRangeMarker( - new ValueMarker(0, java.awt.Color.BLACK, new java.awt.BasicStroke(2))); - - // label to indicate that positive axis is RX traffic - final ValueMarker rxMarker = new ValueMarker(0); - rxMarker.setStroke(new java.awt.BasicStroke(0)); - rxMarker.setLabel("RX"); - rxMarker.setLabelFont(rxMarker.getLabelFont().deriveFont(30f)); - rxMarker.setLabelPaint(java.awt.Color.LIGHT_GRAY); - rxMarker.setLabelAnchor(RectangleAnchor.TOP_RIGHT); - rxMarker.setLabelTextAnchor(TextAnchor.BOTTOM_RIGHT); - xyPlot.addRangeMarker(rxMarker); - - // label to indicate that negative axis is TX traffic - final ValueMarker txMarker = new ValueMarker(0); - txMarker.setStroke(new java.awt.BasicStroke(0)); - txMarker.setLabel("TX"); - txMarker.setLabelFont(txMarker.getLabelFont().deriveFont(30f)); - txMarker.setLabelPaint(java.awt.Color.LIGHT_GRAY); - txMarker.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT); - txMarker.setLabelTextAnchor(TextAnchor.TOP_RIGHT); - xyPlot.addRangeMarker(txMarker); - - mChartComposite = new ChartComposite(mPanel, SWT.BORDER, mChart, - ChartComposite.DEFAULT_WIDTH, ChartComposite.DEFAULT_HEIGHT, - ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH, - ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT, 4096, 4096, true, true, true, true, - false, true); - - final FormData data = new FormData(); - data.top = new FormAttachment(mHeader); - data.left = new FormAttachment(0); - data.bottom = new FormAttachment(70); - data.right = new FormAttachment(100); - mChartComposite.setLayoutData(data); - } - - /** - * Create table showing summary of network activity. - */ - private void createTable() { - mTable = new Table(mPanel, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); - - final FormData data = new FormData(); - data.top = new FormAttachment(mChartComposite); - data.left = new FormAttachment(mChartComposite, 0, SWT.CENTER); - data.bottom = new FormAttachment(100); - mTable.setLayoutData(data); - - mTable.setHeaderVisible(true); - mTable.setLinesVisible(true); - - final IPreferenceStore store = DdmUiPreferences.getStore(); - - TableHelper.createTableColumn(mTable, "", SWT.CENTER, buildSampleText(2), null, null); - TableHelper.createTableColumn( - mTable, "Tag", SWT.LEFT, buildSampleText(32), PREFS_NETWORK_COL_TITLE, store); - TableHelper.createTableColumn(mTable, "RX bytes", SWT.RIGHT, buildSampleText(12), - PREFS_NETWORK_COL_RX_BYTES, store); - TableHelper.createTableColumn(mTable, "RX packets", SWT.RIGHT, buildSampleText(12), - PREFS_NETWORK_COL_RX_PACKETS, store); - TableHelper.createTableColumn(mTable, "TX bytes", SWT.RIGHT, buildSampleText(12), - PREFS_NETWORK_COL_TX_BYTES, store); - TableHelper.createTableColumn(mTable, "TX packets", SWT.RIGHT, buildSampleText(12), - PREFS_NETWORK_COL_TX_PACKETS, store); - - mTableViewer = new TableViewer(mTable); - mTableViewer.setContentProvider(new ContentProvider()); - mTableViewer.setLabelProvider(new LabelProvider()); - } - - /** - * Update {@link #mSpeedMillis} to match {@link #mSpeedCombo} selection. - */ - private void updateSpeed() { - switch (mSpeedCombo.getSelectionIndex()) { - case 0: - mSpeedMillis = 100; - break; - case 1: - mSpeedMillis = 250; - break; - case 2: - mSpeedMillis = 500; - break; - } - } - - /** - * Update if {@link SampleThread} should be actively running. Will create - * new thread or finish existing thread to match requested state. - */ - private void updateRunning(boolean shouldRun) { - final boolean alreadyRunning = mSampleThread != null; - if (alreadyRunning && !shouldRun) { - mSampleThread.finish(); - mSampleThread = null; - - mRunningButton.setText("Start"); - mHeader.pack(); - } else if (!alreadyRunning && shouldRun) { - mSampleThread = new SampleThread(); - mSampleThread.start(); - - mRunningButton.setText("Stop"); - mHeader.pack(); - } - } - - @Override - public void setFocus() { - mPanel.setFocus(); - } - - private static java.awt.Color nextSeriesColor(int index) { - return SERIES_COLORS[index % SERIES_COLORS.length]; - } - - /** - * Find a {@link TrackedItem} that matches the requested UID and tag, or - * create one if none exists. - */ - public TrackedItem findOrCreateTrackedItem(int uid, int tag) { - // try searching for existing item - for (TrackedItem item : mTrackedItems) { - if (item.uid == uid && item.tag == tag) { - return item; - } - } - - // nothing found; create new item - final TrackedItem item = new TrackedItem(uid, tag); - if (item.isTotal()) { - item.color = TOTAL_COLOR; - item.label = "Total"; - } else { - final int size = mTrackedItems.size(); - item.color = nextSeriesColor(size); - Formatter formatter = new Formatter(); - item.label = "0x" + formatter.format("%08x", tag); - formatter.close(); - } - - // create color chip to display as legend in table - item.colorImage = new Image(mDisplay, 20, 20); - final GC gc = new GC(item.colorImage); - gc.setBackground(new org.eclipse.swt.graphics.Color(mDisplay, item.color - .getRed(), item.color.getGreen(), item.color.getBlue())); - gc.fillRectangle(item.colorImage.getBounds()); - gc.dispose(); - - mTrackedItems.add(item); - return item; - } - - /** - * Clear all {@link TrackedItem} and chart history. - */ - public void clearTrackedItems() { - mRxTotalSeries.clear(); - mTxTotalSeries.clear(); - - mRxDetailDataset.clear(); - mTxDetailDataset.clear(); - - mTrackedItems.clear(); - mTableViewer.setInput(mTrackedItems); - } - - /** - * Update the {@link #mRenderer} colors to match {@link TrackedItem#color}. - */ - private void updateSeriesPaint() { - for (TrackedItem item : mTrackedItems) { - final int seriesIndex = mRxDetailDataset.getColumnIndex(item.label); - if (seriesIndex >= 0) { - mRenderer.setSeriesPaint(seriesIndex, item.color); - mRenderer.setSeriesFillPaint(seriesIndex, item.color); - } - } - - // series data is always the same color - final int count = mTotalCollection.getSeriesCount(); - for (int i = 0; i < count; i++) { - mTotalRenderer.setSeriesPaint(i, TOTAL_COLOR); - mTotalRenderer.setSeriesFillPaint(i, TOTAL_COLOR); - } - } - - /** - * Traffic flow being actively tracked, uniquely defined by UID and tag. Can - * record {@link NetworkSnapshot} deltas into {@link TimeSeries} for - * charting, and into summary statistics for {@link Table} display. - */ - private class TrackedItem { - public final int uid; - public final int tag; - - public java.awt.Color color; - public Image colorImage; - - public String label; - public long rxBytes; - public long rxPackets; - public long txBytes; - public long txPackets; - - public TrackedItem(int uid, int tag) { - this.uid = uid; - this.tag = tag; - } - - public boolean isTotal() { - return tag == 0x0; - } - - /** - * Record the given {@link NetworkSnapshot} delta, updating - * {@link TimeSeries} and summary statistics. - * - * @param time Timestamp when delta was observed. - * @param deltaMillis Time duration covered by delta, in milliseconds. - */ - public void recordDelta(Millisecond time, long deltaMillis, NetworkSnapshot.Entry delta) { - final long rxBytesPerSecond = (delta.rxBytes * 1000) / deltaMillis; - final long txBytesPerSecond = (delta.txBytes * 1000) / deltaMillis; - - // record values under correct series - if (isTotal()) { - mRxTotalSeries.addOrUpdate(time, rxBytesPerSecond); - mTxTotalSeries.addOrUpdate(time, -txBytesPerSecond); - } else { - mRxDetailDataset.addValue(rxBytesPerSecond, time, label); - mTxDetailDataset.addValue(-txBytesPerSecond, time, label); - } - - rxBytes += delta.rxBytes; - rxPackets += delta.rxPackets; - txBytes += delta.txBytes; - txPackets += delta.txPackets; - } - } - - @Override - public void deviceSelected() { - // treat as client selection to update enabled states - clientSelected(); - } - - @Override - public void clientSelected() { - mActiveUid = -1; - - final Client client = getCurrentClient(); - if (client != null) { - final int pid = client.getClientData().getPid(); - try { - // map PID to UID from device - final UidParser uidParser = new UidParser(); - getCurrentDevice().executeShellCommand("cat /proc/" + pid + "/status", uidParser); - mActiveUid = uidParser.uid; - } catch (TimeoutException e) { - e.printStackTrace(); - } catch (AdbCommandRejectedException e) { - e.printStackTrace(); - } catch (ShellCommandUnresponsiveException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - clearTrackedItems(); - updateRunning(false); - - final boolean validUid = mActiveUid != -1; - mRunningButton.setEnabled(validUid); - } - - @Override - public void clientChanged(Client client, int changeMask) { - // ignored - } - - /** - * Take a snapshot from {@link #getCurrentDevice()}, recording any delta - * network traffic to {@link TrackedItem}. - */ - public void performSample() { - final IDevice device = getCurrentDevice(); - if (device == null) return; - - try { - final NetworkSnapshotParser parser = new NetworkSnapshotParser(); - device.executeShellCommand("cat " + PROC_XT_QTAGUID, parser); - - if (parser.isError()) { - mDisplay.asyncExec(new Runnable() { - @Override - public void run() { - updateRunning(false); - - final String title = "Problem reading stats"; - final String message = "Problem reading xt_qtaguid network " - + "statistics from selected device."; - Status status = new Status(IStatus.ERROR, "NetworkPanel", 0, message, null); - ErrorDialog.openError(mPanel.getShell(), title, title, status); - } - }); - - return; - } - - final NetworkSnapshot snapshot = parser.getParsedSnapshot(); - - // use first snapshot as baseline - if (mLastSnapshot == null) { - mLastSnapshot = snapshot; - return; - } - - final NetworkSnapshot delta = NetworkSnapshot.subtract(snapshot, mLastSnapshot); - mLastSnapshot = snapshot; - - // perform delta updates over on UI thread - if (!mDisplay.isDisposed()) { - mDisplay.syncExec(new UpdateDeltaRunnable(delta, snapshot.timestamp)); - } - - } catch (TimeoutException e) { - e.printStackTrace(); - } catch (AdbCommandRejectedException e) { - e.printStackTrace(); - } catch (ShellCommandUnresponsiveException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Task that updates UI with given {@link NetworkSnapshot} delta. - */ - private class UpdateDeltaRunnable implements Runnable { - private final NetworkSnapshot mDelta; - private final long mEndTime; - - public UpdateDeltaRunnable(NetworkSnapshot delta, long endTime) { - mDelta = delta; - mEndTime = endTime; - } - - @Override - public void run() { - if (mDisplay.isDisposed()) return; - - final Millisecond time = new Millisecond(new Date(mEndTime)); - for (NetworkSnapshot.Entry entry : mDelta) { - if (mActiveUid != entry.uid) continue; - - final TrackedItem item = findOrCreateTrackedItem(entry.uid, entry.tag); - item.recordDelta(time, mDelta.timestamp, entry); - } - - // remove any historical detail data - final long beforeMillis = mEndTime - HISTORY_MILLIS; - mRxDetailDataset.removeBefore(beforeMillis); - mTxDetailDataset.removeBefore(beforeMillis); - - // trigger refresh from bulk changes above - mRxDetailDataset.fireDatasetChanged(); - mTxDetailDataset.fireDatasetChanged(); - - // update axis to show latest 30 second time period - mDomainAxis.setRange(mEndTime - HISTORY_MILLIS, mEndTime); - - updateSeriesPaint(); - - // kick table viewer to update - mTableViewer.setInput(mTrackedItems); - } - } - - /** - * Parser that extracts UID from remote {@code /proc/pid/status} file. - */ - private static class UidParser extends MultiLineReceiver { - public int uid = -1; - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - if (line.startsWith("Uid:")) { - // we care about the "real" UID - final String[] cols = line.split("\t"); - uid = Integer.parseInt(cols[1]); - } - } - } - } - - /** - * Parser that populates {@link NetworkSnapshot} based on contents of remote - * {@link NetworkPanel#PROC_XT_QTAGUID} file. - */ - private static class NetworkSnapshotParser extends MultiLineReceiver { - private NetworkSnapshot mSnapshot; - - public NetworkSnapshotParser() { - mSnapshot = new NetworkSnapshot(System.currentTimeMillis()); - } - - public boolean isError() { - return mSnapshot == null; - } - - public NetworkSnapshot getParsedSnapshot() { - return mSnapshot; - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - if (line.endsWith("No such file or directory")) { - mSnapshot = null; - return; - } - - // ignore header line - if (line.startsWith("idx")) { - continue; - } - - final String[] cols = line.split(" "); - if (cols.length < 9) continue; - - // iface and set are currently ignored, which groups those - // entries together. - final NetworkSnapshot.Entry entry = new NetworkSnapshot.Entry(); - - entry.iface = null; //cols[1]; - entry.uid = Integer.parseInt(cols[3]); - entry.set = -1; //Integer.parseInt(cols[4]); - entry.tag = kernelToTag(cols[2]); - entry.rxBytes = Long.parseLong(cols[5]); - entry.rxPackets = Long.parseLong(cols[6]); - entry.txBytes = Long.parseLong(cols[7]); - entry.txPackets = Long.parseLong(cols[8]); - - mSnapshot.combine(entry); - } - } - - /** - * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming - * format like {@code 0x7fffffff00000000}. - * Matches code in android.server.NetworkManagementSocketTagger - */ - public static int kernelToTag(String string) { - int length = string.length(); - if (length > 10) { - return Long.decode(string.substring(0, length - 8)).intValue(); - } else { - return 0; - } - } - } - - /** - * Parsed snapshot of {@link NetworkPanel#PROC_XT_QTAGUID} at specific time. - */ - private static class NetworkSnapshot implements Iterable<NetworkSnapshot.Entry> { - private ArrayList<Entry> mStats = new ArrayList<Entry>(); - - public final long timestamp; - - /** Single parsed statistics row. */ - public static class Entry { - public String iface; - public int uid; - public int set; - public int tag; - public long rxBytes; - public long rxPackets; - public long txBytes; - public long txPackets; - - public boolean isEmpty() { - return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0; - } - } - - public NetworkSnapshot(long timestamp) { - this.timestamp = timestamp; - } - - public void clear() { - mStats.clear(); - } - - /** - * Combine the given {@link Entry} with any existing {@link Entry}, or - * insert if none exists. - */ - public void combine(Entry entry) { - final Entry existing = findEntry(entry.iface, entry.uid, entry.set, entry.tag); - if (existing != null) { - existing.rxBytes += entry.rxBytes; - existing.rxPackets += entry.rxPackets; - existing.txBytes += entry.txBytes; - existing.txPackets += entry.txPackets; - } else { - mStats.add(entry); - } - } - - @Override - public Iterator<Entry> iterator() { - return mStats.iterator(); - } - - public Entry findEntry(String iface, int uid, int set, int tag) { - for (Entry entry : mStats) { - if (entry.uid == uid && entry.set == set && entry.tag == tag - && equal(entry.iface, iface)) { - return entry; - } - } - return null; - } - - /** - * Subtract the two given {@link NetworkSnapshot} objects, returning the - * delta between them. - */ - public static NetworkSnapshot subtract(NetworkSnapshot left, NetworkSnapshot right) { - final NetworkSnapshot result = new NetworkSnapshot(left.timestamp - right.timestamp); - - // for each row on left, subtract value from right side - for (Entry leftEntry : left) { - final Entry rightEntry = right.findEntry( - leftEntry.iface, leftEntry.uid, leftEntry.set, leftEntry.tag); - if (rightEntry == null) continue; - - final Entry resultEntry = new Entry(); - resultEntry.iface = leftEntry.iface; - resultEntry.uid = leftEntry.uid; - resultEntry.set = leftEntry.set; - resultEntry.tag = leftEntry.tag; - resultEntry.rxBytes = leftEntry.rxBytes - rightEntry.rxBytes; - resultEntry.rxPackets = leftEntry.rxPackets - rightEntry.rxPackets; - resultEntry.txBytes = leftEntry.txBytes - rightEntry.txBytes; - resultEntry.txPackets = leftEntry.txPackets - rightEntry.txPackets; - - result.combine(resultEntry); - } - - return result; - } - } - - /** - * Provider of {@link #mTrackedItems}. - */ - private class ContentProvider implements IStructuredContentProvider { - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - - @Override - public void dispose() { - // pass - } - - @Override - public Object[] getElements(Object inputElement) { - return mTrackedItems.toArray(); - } - } - - /** - * Provider of labels for {@Link TrackedItem} values. - */ - private static class LabelProvider implements ITableLabelProvider { - private final DecimalFormat mFormat = new DecimalFormat("#,###"); - - @Override - public Image getColumnImage(Object element, int columnIndex) { - if (element instanceof TrackedItem) { - final TrackedItem item = (TrackedItem) element; - switch (columnIndex) { - case 0: - return item.colorImage; - } - } - return null; - } - - @Override - public String getColumnText(Object element, int columnIndex) { - if (element instanceof TrackedItem) { - final TrackedItem item = (TrackedItem) element; - switch (columnIndex) { - case 0: - return null; - case 1: - return item.label; - case 2: - return mFormat.format(item.rxBytes); - case 3: - return mFormat.format(item.rxPackets); - case 4: - return mFormat.format(item.txBytes); - case 5: - return mFormat.format(item.txPackets); - } - } - return null; - } - - @Override - public void addListener(ILabelProviderListener listener) { - // pass - } - - @Override - public void dispose() { - // pass - } - - @Override - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - @Override - public void removeListener(ILabelProviderListener listener) { - // pass - } - } - - /** - * Format that displays simplified byte units for when given values are - * large enough. - */ - private static class BytesFormat extends NumberFormat { - private final String[] mUnits; - private final DecimalFormat mFormat = new DecimalFormat("#.#"); - - public BytesFormat(boolean perSecond) { - if (perSecond) { - mUnits = new String[] { "B/s", "KB/s", "MB/s" }; - } else { - mUnits = new String[] { "B", "KB", "MB" }; - } - } - - @Override - public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) { - double value = Math.abs(number); - - int i = 0; - while (value > 1024 && i < mUnits.length - 1) { - value /= 1024; - i++; - } - - toAppendTo.append(mFormat.format(value)); - toAppendTo.append(mUnits[i]); - - return toAppendTo; - } - - @Override - public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) { - return format((long) number, toAppendTo, pos); - } - - @Override - public Number parse(String source, ParsePosition parsePosition) { - return null; - } - } - - public static boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); - } - - /** - * Build stub string of requested length, usually for measurement. - */ - private static String buildSampleText(int length) { - final StringBuilder builder = new StringBuilder(length); - for (int i = 0; i < length; i++) { - builder.append("X"); - } - return builder.toString(); - } - - /** - * Dataset that contains live measurements. Exposes - * {@link #removeBefore(long)} to efficiently remove old data, and enables - * batched {@link #fireDatasetChanged()} events. - */ - public static class LiveTimeTableXYDataset extends AbstractIntervalXYDataset implements - TableXYDataset { - private DefaultKeyedValues2D mValues = new DefaultKeyedValues2D(true); - - /** - * Caller is responsible for triggering {@link #fireDatasetChanged()}. - */ - public void addValue(Number value, TimePeriod rowKey, String columnKey) { - mValues.addValue(value, rowKey, columnKey); - } - - /** - * Caller is responsible for triggering {@link #fireDatasetChanged()}. - */ - public void removeBefore(long beforeMillis) { - while(mValues.getRowCount() > 0) { - final TimePeriod period = (TimePeriod) mValues.getRowKey(0); - if (period.getEnd().getTime() < beforeMillis) { - mValues.removeRow(0); - } else { - break; - } - } - } - - public int getColumnIndex(String key) { - return mValues.getColumnIndex(key); - } - - public void clear() { - mValues.clear(); - fireDatasetChanged(); - } - - @Override - public void fireDatasetChanged() { - super.fireDatasetChanged(); - } - - @Override - public int getItemCount() { - return mValues.getRowCount(); - } - - @Override - public int getItemCount(int series) { - return mValues.getRowCount(); - } - - @Override - public int getSeriesCount() { - return mValues.getColumnCount(); - } - - @Override - public Comparable getSeriesKey(int series) { - return mValues.getColumnKey(series); - } - - @Override - public double getXValue(int series, int item) { - final TimePeriod period = (TimePeriod) mValues.getRowKey(item); - return period.getStart().getTime(); - } - - @Override - public double getStartXValue(int series, int item) { - return getXValue(series, item); - } - - @Override - public double getEndXValue(int series, int item) { - return getXValue(series, item); - } - - @Override - public Number getX(int series, int item) { - return getXValue(series, item); - } - - @Override - public Number getStartX(int series, int item) { - return getXValue(series, item); - } - - @Override - public Number getEndX(int series, int item) { - return getXValue(series, item); - } - - @Override - public Number getY(int series, int item) { - return mValues.getValue(item, series); - } - - @Override - public Number getStartY(int series, int item) { - return getY(series, item); - } - - @Override - public Number getEndY(int series, int item) { - return getY(series, item); - } - } -} |