package net.wigle.wigleandroid.model;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import android.annotation.SuppressLint;
import android.net.wifi.ScanResult;
import com.google.android.gms.maps.model.LatLng;
import com.google.maps.android.clustering.ClusterItem;
/**
* network data. not thread-safe.
*/
@SuppressLint("UseSparseArrays")
public final class Network implements ClusterItem {
private final String bssid;
private final String ssid;
private final int frequency;
private final String capabilities;
private int level;
private final Integer channel;
private final String showCapabilities;
private final int crypto;
private final NetworkType type;
private LatLng geoPoint;
private boolean isNew;
private String detail;
private final long constructionTime = System.currentTimeMillis();
private static final String BAR_STRING = " | ";
private static final String DASH_STRING = " - ";
private static final String WPA_CAP = "[WPA";
private static final String WEP_CAP = "[WEP";
// faster than enums
public static final int CRYPTO_NONE = 0;
public static final int CRYPTO_WEP = 1;
public static final int CRYPTO_WPA = 2;
private static final Map<Integer,Integer> freqToChan;
static {
Map<Integer,Integer> freqToChanTemp = new HashMap<>();
for ( int i = 237; i <= 255; i++ ) {
freqToChanTemp.put( 2312 + 5 * (i - 237), i );
}
for ( int i = 0; i <= 13; i++ ) {
freqToChanTemp.put(2407 + (5 * i), i);
}
freqToChanTemp.put(2484, 14);
freqToChanTemp.put(5170, 34);
freqToChanTemp.put(5180, 36);
freqToChanTemp.put(5190, 38);
freqToChanTemp.put(5200, 40);
freqToChanTemp.put(5210, 42);
freqToChanTemp.put(5220, 44);
freqToChanTemp.put(5230, 46);
freqToChanTemp.put(5240, 48);
freqToChanTemp.put(5260, 52);
freqToChanTemp.put(5280, 56);
freqToChanTemp.put(5300, 58);
freqToChanTemp.put(5320, 60);
freqToChanTemp.put(5500, 100);
freqToChanTemp.put(5520, 104);
freqToChanTemp.put(5540, 108);
freqToChanTemp.put(5560, 112);
freqToChanTemp.put(5570, 116);
freqToChanTemp.put(5600, 120);
freqToChanTemp.put(5620, 124);
freqToChanTemp.put(5640, 128);
freqToChanTemp.put(5660, 132);
freqToChanTemp.put(5680, 136);
freqToChanTemp.put(5700, 140);
freqToChanTemp.put(5745, 149);
freqToChanTemp.put(5765, 153);
freqToChanTemp.put(5785, 157);
freqToChanTemp.put(5805, 161);
freqToChanTemp.put(5825, 165);
freqToChan = Collections.unmodifiableMap( freqToChanTemp );
}
/**
* convenience constructor
* @param scanResult a result from a wifi scan
*/
public Network( final ScanResult scanResult ) {
this( scanResult.BSSID, scanResult.SSID, scanResult.frequency, scanResult.capabilities,
scanResult.level, NetworkType.WIFI );
}
public Network( final String bssid, final String ssid, final int frequency, final String capabilities,
final int level, final NetworkType type ) {
this.bssid = ( bssid == null ) ? "" : bssid.toLowerCase(Locale.US);
this.ssid = ( ssid == null ) ? "" : ssid;
this.frequency = frequency;
this.capabilities = ( capabilities == null ) ? "" : capabilities;
this.level = level;
this.type = type;
this.channel = freqToChan.get( frequency );
if ( ! NetworkType.WIFI.equals( type ) ) {
int semicolon = this.capabilities.lastIndexOf(";");
if ( semicolon > 0 ) {
this.showCapabilities = this.capabilities.substring(0, semicolon);
}
else {
this.showCapabilities = this.capabilities;
}
}
else if ( this.capabilities.length() > 16 ) {
this.showCapabilities = this.capabilities.replaceAll("(\\[\\w+)\\-.*?\\]", "$1]");
}
else {
this.showCapabilities = null;
}
if (this.capabilities.contains(WPA_CAP)) {
crypto = CRYPTO_WPA;
}
else if (this.capabilities.contains(WEP_CAP)) {
crypto = CRYPTO_WEP;
}
else {
crypto = CRYPTO_NONE;
}
}
public String getBssid() {
return bssid;
}
public String getSsid() {
return ssid;
}
public int getFrequency() {
return frequency;
}
public String getCapabilities() {
return capabilities;
}
public String getShowCapabilities() {
if ( showCapabilities == null ) {
return capabilities;
}
return showCapabilities;
}
public int getLevel() {
return level;
}
public NetworkType getType() {
return type;
}
public Integer getChannel() {
return channel;
}
public void setLevel( final int level ) {
this.level = level;
}
public void setIsNew() {
this.isNew = true;
}
public boolean isNew() {
return isNew;
}
/**
* get crypto category, one of CRYPTO_* defined in this class.
* @return integer corresponding to an encryption category
*/
public int getCrypto() {
return crypto;
}
public long getConstructionTime() {
return constructionTime;
}
public String getDetail() {
if ( detail == null ) {
final Integer chan = channel != null ? channel : frequency;
final StringBuilder detailBuild = new StringBuilder( 40 );
detailBuild.append( BAR_STRING ).append( bssid );
detailBuild.append( DASH_STRING );
if ( NetworkType.WIFI.equals(type) ) {
detailBuild.append( chan );
}
else {
detailBuild.append( type );
}
detailBuild.append( DASH_STRING ).append( getShowCapabilities() );
detail = detailBuild.toString();
}
return detail;
}
public void setLatLng(LatLng geoPoint) {
this.geoPoint = geoPoint;
}
public LatLng getLatLng() {
return geoPoint;
}
public String getOui(final OUI oui) {
String retval = "";
final String lookup = getBssid().replace(":", "").toUpperCase();
if (oui != null && lookup.length() >= 9) {
retval = oui.getOui(lookup.substring(0, 9));
if (retval == null) retval = oui.getOui(lookup.substring(0, 7));
if (retval == null) retval = oui.getOui(lookup.substring(0, 6));
}
return retval == null ? "" : retval;
}
@Override
public LatLng getPosition() {
return geoPoint;
}
@Override
public int hashCode() {
return bssid.hashCode();
}
@Override
public boolean equals(final Object other) {
if (other instanceof Network) {
final Network o = (Network) other;
return bssid.equals(o.bssid);
}
return false;
}
}