/* (C) 2012 Pragmatic Software
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/
*/
package com.googlecode.networklog;
import android.os.Bundle;
import android.graphics.Color;
import android.graphics.drawable.shapes.Shape;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.ShapeDrawable;
import android.util.Log;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import com.jjoe64.graphview.GraphView.GraphViewData;
import com.jjoe64.graphview.GraphView.GraphViewSeries;
public class AppTimelineGraph extends GraphActivity
{
private int app_uid;
private String src_addr;
private int src_port;
private String dst_addr;
private int dst_port;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
if(extras != null) {
app_uid = extras.getInt("app_uid");
src_addr = extras.getString("src_addr");
src_port = extras.getInt("src_port");
dst_addr = extras.getString("dst_addr");
dst_port = extras.getInt("dst_port");
}
int index = NetworkLog.appFragment.getItemByAppUid(app_uid);
if(index < 0) {
// alert dialog
finish();
return;
}
AppFragment.GroupItem item = NetworkLog.appFragment.groupDataBuffer.get(index);
graphView.setTitle(item.toString() + getString(R.string.graph_timeline));
buildSeries(interval, viewsize);
}
private class HostPort {
String host;
int port;
}
public void buildSeries(double timeFrameSize, double viewSize) {
if(instanceData != null) {
graphView.graphSeries = instanceData.graphSeries;
} else {
HashMap<String, ArrayList<PacketGraphItem>> hostMap = new HashMap<String, ArrayList<PacketGraphItem>>();
HashMap<String, HostPort> addressMap = new HashMap<String, HostPort>();
ArrayList<PacketGraphItem> packetList;
CharArray charBuffer = new CharArray(256);
String hostKey;
String address;
int port;
HostPort hostPort;
graphView.graphSeries.clear();
if(NetworkLog.logFragment == null || NetworkLog.logFragment.listData == null || NetworkLog.logFragment.listData.size() == 0) {
SysUtils.showError(this, getString(R.string.graph_error_nodata_title), getString(R.string.graph_error_nodata_text));
finish();
return;
}
synchronized(NetworkLog.logFragment.listData) {
for(LogFragment.ListItem item : NetworkLog.logFragment.listData) {
if(item.app.uid == app_uid) {
//Log.d("NetworkLog", "Testing packet [" + (item.in == null ? "null" : item.in) + "] " + item.srcAddr + ":" + item.srcPort + " -> [" + (item.out == null ? "null" : item.out) + "] " + item.dstAddr + ":" + item.dstPort);
try {
charBuffer.reset();
if(item.in != null && item.in.length() != 0) {
charBuffer.append(item.srcAddr).append(':').append(item.srcPort);
address = item.srcAddr;
port = item.srcPort;
} else {
charBuffer.append(item.dstAddr).append(':').append(item.dstPort);
address = item.dstAddr;
port = item.dstPort;
}
} catch (ArrayIndexOutOfBoundsException e) {
Log.e("NetworkLog", "[AppTimelimeGraph] charBuffer too long, skipping entry " + item, e);
continue;
}
hostKey = StringPool.get(charBuffer);
hostPort = addressMap.get(hostKey);
if(hostPort == null) {
hostPort = new HostPort();
hostPort.host = address;
hostPort.port = port;
addressMap.put(hostKey, hostPort);
//Log.d("NetworkLog", "Adding new hostPort: " + hostKey + " == " + address + ":" + port);
}
packetList = hostMap.get(hostKey);
if(packetList == null) {
packetList = new ArrayList<PacketGraphItem>();
hostMap.put(hostKey, packetList);
}
packetList.add(new PacketGraphItem(item.timestamp, item.len));
}
}
}
if(hostMap.size() == 0) {
SysUtils.showError(this, getString(R.string.graph_error_nodata_title), getString(R.string.graph_error_nodata_text));
finish();
return;
}
int color = 0;
float density = getResources().getDisplayMetrics().density;
Shape rect = new RectShape();
int intrinsicLength = (int)(18 * (density + 0.5));
for(Map.Entry<String, ArrayList<PacketGraphItem>> entry : hostMap.entrySet()) {
hostKey = entry.getKey();
packetList = entry.getValue();
if(MyLog.enabled) {
MyLog.d("number of packets for " + hostKey + ": " + packetList.size());
}
ArrayList<PacketGraphItem> graphData = new ArrayList<PacketGraphItem>();
double nextTimeFrame = 0;
double frameLen = 1; // len for this time frame
for(PacketGraphItem data : packetList) {
if(nextTimeFrame == 0) {
// first plot
graphData.add(new PacketGraphItem(data.timestamp - 1, 1));
graphData.add(new PacketGraphItem(data.timestamp, data.len));
// set up first time frame
nextTimeFrame = data.timestamp + timeFrameSize;
frameLen = data.len;
// get next data
continue;
}
if(data.timestamp <= nextTimeFrame) {
// data within current time frame, add to frame len
frameLen += data.len;
// get next data
continue;
} else {
// data outside current time frame
// signifies end of frame
// plot frame len
graphData.add(new PacketGraphItem(nextTimeFrame, frameLen));
// set up next time frame
nextTimeFrame += timeFrameSize;
frameLen = 1;
// test for gap
if(data.timestamp > nextTimeFrame) {
// data is past this time frame, plot zero here
graphData.add(new PacketGraphItem(nextTimeFrame, frameLen));
if((data.timestamp - timeFrameSize) > nextTimeFrame) {
graphData.add(new PacketGraphItem(data.timestamp - timeFrameSize, 1));
}
nextTimeFrame = data.timestamp;
frameLen = data.len;
graphData.add(new PacketGraphItem(nextTimeFrame, frameLen));
nextTimeFrame += timeFrameSize;
frameLen = 1;
continue;
} else {
// data is within this frame, add len
frameLen = data.len;
}
}
}
// post plot
graphData.add(new PacketGraphItem(nextTimeFrame, frameLen));
// post zero plot
graphData.add(new PacketGraphItem(nextTimeFrame + timeFrameSize, 1));
GraphViewData[] seriesData = new GraphViewData[graphData.size()];
int i = 0;
for(PacketGraphItem graphItem : graphData) {
seriesData[i] = new GraphViewData(graphItem.timestamp, graphItem.len);
i++;
}
hostPort = addressMap.get(hostKey);
//Log.d("NetworkLog", "Got from hostMap: " + hostKey + " == " + hostPort.host + ":" + hostPort.port);
final String portString;
if(NetworkLog.resolvePorts) {
portString = NetworkLog.resolver.resolveService(String.valueOf(hostPort.port));
} else {
portString = String.valueOf(hostPort.port);
}
String addressString;
if(NetworkLog.resolveHosts) {
addressString = NetworkLog.resolver.getResolvedAddress(hostPort.host);
if(addressString == null) {
String label = hostPort.host + ":" + portString;
final int hashCode = label.hashCode();
NetworkResolverUpdater updater = new NetworkResolverUpdater() {
public void run() {
for(LegendItem legend : legendData) {
if(legend.mHashCode == hashCode) {
legend.mName = resolved + ":" + portString;
refreshLegendAdapter();
break;
}
}
}
};
addressString = NetworkLog.resolver.resolveAddress(hostPort.host, updater);
if(addressString == null) {
addressString = hostPort.host;
}
}
} else {
addressString = hostPort.host;
}
String label = addressString + ":" + portString;
int hashCode = label.hashCode();
graphView.addSeries(new GraphViewSeries(hashCode, label, Color.parseColor(getResources().getString(Colors.distinctColor[color])), seriesData));
boolean enabled = true;
boolean exists = false;
for(LegendItem legend : legendData) {
if(legend.mHashCode == hashCode) {
enabled = legend.mEnabled;
exists = true;
break;
}
}
if(exists == false) {
ShapeDrawable shape = new ShapeDrawable(rect);
shape.getPaint().setColor(Color.parseColor(getResources().getString(Colors.distinctColor[color])));
shape.setIntrinsicWidth(intrinsicLength);
shape.setIntrinsicHeight(intrinsicLength);
LegendItem legend = new LegendItem();
legend.mIcon = shape;
legend.mHashCode = hashCode;
legend.mName = label;
legend.mEnabled = true;
legendData.add(legend);
//Log.d("NetworkLog", "Adding new legend: " + label);
}
graphView.setSeriesEnabled(hashCode, enabled);
color++;
if(color >= Colors.distinctColor.length) {
color = 0;
}
}
}
double minX = graphView.getMinX(true);
double maxX = graphView.getMaxX(true);
double viewStart = maxX - viewSize;
if(instanceData != null) {
viewStart = instanceData.viewportStart;
viewSize = instanceData.viewsize;
}
if(viewStart < minX) {
viewStart = minX;
}
if(viewStart + viewSize > maxX) {
viewSize = maxX - viewStart;
}
graphView.setViewPort(viewStart, viewSize);
graphView.invalidateLabels();
graphView.invalidate();
}
}