package be.shouldit.proxy.lib; import android.net.NetworkInfo; import android.net.Uri; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import org.json.JSONException; import org.json.JSONObject; import java.util.Arrays; import java.util.UUID; import be.shouldit.proxy.lib.enums.CheckStatusValues; import be.shouldit.proxy.lib.enums.PskType; import be.shouldit.proxy.lib.enums.SecurityType; import be.shouldit.proxy.lib.reflection.android.ProxySetting; import be.shouldit.proxy.lib.utils.ProxyUtils; import timber.log.Timber; public class WiFiApConfig implements Comparable<WiFiApConfig>, Parcelable { private static final String TAG = WiFiApConfig.class.getSimpleName(); public static final int INVALID_NETWORK_ID = -1; public static final String LOCAL_EXCL_LIST = ""; public static final int LOCAL_PORT = -1; public static final String LOCAL_HOST = "localhost"; public static final int[] STATE_SECURED = {R.attr.state_encrypted}; public static final int[] STATE_NONE = {}; private final UUID id; private final APLNetworkId internalWifiNetworkId; private ProxyStatus status; private ProxySetting proxySetting; private String proxyHost; private Integer proxyPort; private String stringProxyExclusionList; private String pacFileUri; private String[] parsedProxyExclusionList; /* AccessPoint class fields */ // public AccessPoint ap; private String ssid; private String bssid; private SecurityType securityType; private int networkId; private PskType pskType = PskType.UNKNOWN; private transient WifiConfiguration wifiConfig; private WifiInfo mInfo; private int mRssi; private NetworkInfo.DetailedState mState; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeSerializable(this.id); dest.writeParcelable(this.internalWifiNetworkId, 0); dest.writeParcelable(this.status, flags); dest.writeInt(this.proxySetting == null ? -1 : this.proxySetting.ordinal()); dest.writeString(this.proxyHost); dest.writeValue(this.proxyPort); dest.writeString(this.stringProxyExclusionList); dest.writeString(this.pacFileUri); dest.writeStringArray(this.parsedProxyExclusionList); dest.writeString(this.ssid); dest.writeString(this.bssid); dest.writeInt(this.securityType == null ? -1 : this.securityType.ordinal()); dest.writeInt(this.networkId); dest.writeInt(this.pskType == null ? -1 : this.pskType.ordinal()); dest.writeParcelable(this.wifiConfig, 0); dest.writeParcelable(this.mInfo, 0); dest.writeInt(this.mRssi); dest.writeInt(this.mState == null ? -1 : this.mState.ordinal()); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof WiFiApConfig)) return false; WiFiApConfig that = (WiFiApConfig) o; if (mRssi != that.mRssi) return false; if (networkId != that.networkId) return false; if (bssid != null ? !bssid.equals(that.bssid) : that.bssid != null) return false; if (id != null ? !id.equals(that.id) : that.id != null) return false; if (internalWifiNetworkId != null ? !internalWifiNetworkId.equals(that.internalWifiNetworkId) : that.internalWifiNetworkId != null) return false; if (mInfo != null ? !mInfo.equals(that.mInfo) : that.mInfo != null) return false; if (mState != that.mState) return false; if (pacFileUri != null ? !pacFileUri.equals(that.pacFileUri) : that.pacFileUri != null) return false; if (!Arrays.equals(parsedProxyExclusionList, that.parsedProxyExclusionList)) return false; if (proxyHost != null ? !proxyHost.equals(that.proxyHost) : that.proxyHost != null) return false; if (proxyPort != null ? !proxyPort.equals(that.proxyPort) : that.proxyPort != null) return false; if (proxySetting != that.proxySetting) return false; if (pskType != that.pskType) return false; if (securityType != that.securityType) return false; if (ssid != null ? !ssid.equals(that.ssid) : that.ssid != null) return false; if (status != null ? !status.equals(that.status) : that.status != null) return false; if (stringProxyExclusionList != null ? !stringProxyExclusionList.equals(that.stringProxyExclusionList) : that.stringProxyExclusionList != null) return false; // Cannot call equals on WifiConfiguration class, since it doesn't override the base method // if (wifiConfig != null ? !wifiConfig.equals(that.wifiConfig) : that.wifiConfig != null) // return false; return true; } private WiFiApConfig(Parcel in) { this.id = (UUID) in.readSerializable(); this.internalWifiNetworkId = in.readParcelable(APLNetworkId.class.getClassLoader()); this.status = in.readParcelable(ProxyStatus.class.getClassLoader()); int tmpProxySetting = in.readInt(); this.proxySetting = tmpProxySetting < 0 || tmpProxySetting >= ProxySetting.values().length ? null : ProxySetting.values()[tmpProxySetting]; this.proxyHost = in.readString(); this.proxyPort = (Integer) in.readValue(Integer.class.getClassLoader()); this.stringProxyExclusionList = in.readString(); this.pacFileUri = in.readString(); this.parsedProxyExclusionList = in.createStringArray(); this.ssid = in.readString(); this.bssid = in.readString(); int tmpSecurityType = in.readInt(); this.securityType = tmpSecurityType < 0 || tmpSecurityType >= SecurityType.values().length ? null : SecurityType.values()[tmpSecurityType]; this.networkId = in.readInt(); int tmpPskType = in.readInt(); this.pskType = tmpPskType < 0 || tmpPskType >= PskType.values().length ? null : PskType.values()[tmpPskType]; this.wifiConfig = in.readParcelable(WifiConfiguration.class.getClassLoader()); this.mInfo = in.readParcelable(WifiInfo.class.getClassLoader()); this.mRssi = in.readInt(); int tmpMState = in.readInt(); this.mState = tmpMState < 0 || tmpMState >= NetworkInfo.DetailedState.values().length ? null : NetworkInfo.DetailedState.values()[tmpMState]; } public static final Creator<WiFiApConfig> CREATOR = new Creator<WiFiApConfig>() { public WiFiApConfig createFromParcel(Parcel source) {return new WiFiApConfig(source);} public WiFiApConfig[] newArray(int size) {return new WiFiApConfig[size];} }; public WiFiApConfig(WifiConfiguration wifiConf, ProxySetting setting, String host, Integer port, String exclusionList, Uri pacFile) { if (wifiConf == null) throw new IllegalArgumentException("WifiConfiguration parameter cannot be null"); id = UUID.randomUUID(); setProxySetting(setting); proxyHost = host; proxyPort = port; setProxyExclusionString(exclusionList); pacFileUri = pacFile.toString(); ssid = (wifiConf.SSID == null ? "" : removeDoubleQuotes(wifiConf.SSID)); bssid = wifiConf.BSSID; securityType = ProxyUtils.getSecurity(wifiConf); networkId = wifiConf.networkId; mRssi = Integer.MAX_VALUE; wifiConfig = wifiConf; internalWifiNetworkId = new APLNetworkId(getSSID(), getSecurityType()); status = new ProxyStatus(); } public boolean updateScanResults(ScanResult result) { if (getSSID().equals(result.SSID) && getSecurityType() == ProxyUtils.getSecurity(result)) { if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) { int oldLevel = getLevel(); mRssi = result.level; } // This flag only comes from scans, is not easily saved in config if (getSecurityType() == SecurityType.SECURITY_PSK) { pskType = ProxyUtils.getPskType(result); } return true; } return false; } public void updateWifiInfo(WifiInfo info, NetworkInfo.DetailedState state) { if (info != null && getNetworkId() != INVALID_NETWORK_ID && getNetworkId() == info.getNetworkId()) { mRssi = info.getRssi(); mInfo = info; mState = state; } else if (mInfo != null) { mInfo = null; mState = null; } } public boolean updateProxyConfiguration(WiFiApConfig updated) { //TODO: Add all required fields for updating an old configuration with an updated version if (!this.isSameConfiguration(updated)) { Timber.d("Updating proxy configuration: \n%s\n%s",this.toShortString(), updated.toShortString()); setProxySetting(updated.getProxySetting()); proxyHost = updated.proxyHost; proxyPort = updated.proxyPort; stringProxyExclusionList = updated.getStringProxyExclusionList(); parsedProxyExclusionList = ProxyUtils.parseExclusionList(getStringProxyExclusionList()); pacFileUri = updated.pacFileUri; getStatus().clear(); Timber.d("Updated proxy configuration: \n%s\n%s", this.toShortString(), updated.toShortString()); return true; } else { // LogWrapper.d(TAG,"No need to update proxy configuration: " + this.toShortString()); return false; } } public boolean isValidProxyConfiguration() { return ProxyUtils.isValidProxyConfiguration(this); } public void setProxySetting(ProxySetting setting) { synchronized (getId()) { proxySetting = setting; } } public ProxySetting getProxySetting() { synchronized (getId()) { return proxySetting; } } public void setProxyHost(String host) { proxyHost = host; } public void setProxyPort(Integer port) { proxyPort = port; } public void setProxyExclusionString(String exList) { stringProxyExclusionList = exList; parsedProxyExclusionList = ProxyUtils.parseExclusionList(exList); } public boolean isSameConfiguration(Object another) { if (!(another instanceof WiFiApConfig)) { Timber.e("Not a WiFiApConfig object"); return false; } WiFiApConfig anotherConf = (WiFiApConfig) another; if (!this.proxySetting.equals(anotherConf.proxySetting)) { Timber.d("Different proxy settings toggle status: '%s' - '%s'", this.proxySetting, anotherConf.proxySetting); return false; } if (this.proxyHost != null && anotherConf.proxyHost != null) { if (!this.proxyHost.equalsIgnoreCase(anotherConf.proxyHost)) { Timber.d("Different proxy host value: '%s' - '%s'", this.proxyHost, anotherConf.proxyHost); return false; } } else if (this.proxyHost != anotherConf.proxyHost) { if ((this.proxyHost == null || this.proxyHost.equals("") && (anotherConf.proxyHost == null || anotherConf.proxyHost.equals("")))) { /** Can happen when a partial configuration is written on the device: * - ProxySettings enabled but no proxyHost and proxyPort are filled */ } else { Timber.d("Different proxy host set"); Timber.d(TextUtils.isEmpty(this.proxyHost) ? "" : this.proxyHost); Timber.d(TextUtils.isEmpty(anotherConf.proxyHost) ? "" : anotherConf.proxyHost); return false; } } if (this.proxyPort != null && anotherConf.proxyPort != null) { if (!this.proxyPort.equals(anotherConf.proxyPort)) { Timber.d("Different proxy port value: '%d' - '%d'", this.proxyPort, anotherConf.proxyPort); return false; } } else if (this.proxyPort != anotherConf.proxyPort) { if ((this.proxyPort == null || this.proxyPort == 0) && (anotherConf.proxyPort == null || anotherConf.proxyPort == 0)) { /** Can happen when a partial configuration is written on the device: * - ProxySettings enabled but no proxyHost and proxyPort are filled */ } else { Timber.d("Different proxy port set"); return false; } } if (this.getStringProxyExclusionList() != null && anotherConf.getStringProxyExclusionList() != null) { if (!this.getStringProxyExclusionList().equalsIgnoreCase(anotherConf.getStringProxyExclusionList())) { Timber.d("Different proxy exclusion list value: '%s' - '%s'", this.getStringProxyExclusionList(), anotherConf.getStringProxyExclusionList()); return false; } } else if (this.getStringProxyExclusionList() != anotherConf.getStringProxyExclusionList()) { if (TextUtils.isEmpty(this.getStringProxyExclusionList()) && TextUtils.isEmpty(anotherConf.getStringProxyExclusionList())) { /** Can happen when a partial configuration is written on the device: * - ProxySettings enabled but no proxyHost and proxyPort are filled */ } else { Timber.d("Different proxy exclusion list set"); return false; } } // LogWrapper.d(TAG,"Same proxy configuration: \n" + this.toShortString() + "\n" + anotherConf.toShortString()); return true; } @Override public int compareTo(WiFiApConfig wiFiApConfig) { if (!(wiFiApConfig instanceof WiFiApConfig)) { return 1; } return WifiApConfigComparator.compareWifiAp(this, (WiFiApConfig) wiFiApConfig); } public boolean isActive() { return mInfo != null; } public boolean isReachable() { return mRssi != Integer.MAX_VALUE; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(String.format("SSID: %s\n", getSSID())); sb.append(String.format("Internal ID: %s\n", getId().toString())); sb.append(String.format("Proxy setting: %s\n", getProxySetting().toString())); sb.append(String.format("Proxy: %s\n", getProxyStatusString())); sb.append(String.format("Is current network: %B\n", isActive())); sb.append(String.format("Proxy status checker results: %s\n", getStatus().toString())); if (APL.getConnectivityManager().getActiveNetworkInfo() != null) { sb.append(String.format("Network Info: %s\n", APL.getConnectivityManager().getActiveNetworkInfo())); } return sb.toString(); } public JSONObject toJSON() { JSONObject jsonObject = new JSONObject(); try { jsonObject.put("ID", getId().toString()); jsonObject.put("SSID", getSSID()); jsonObject.put("proxy_setting", getProxySetting().toString()); jsonObject.put("proxy_status", getProxyStatusString()); jsonObject.put("is_current", isActive()); jsonObject.put("status", getStatus().toJSON()); if (APL.getConnectivityManager().getActiveNetworkInfo() != null) { jsonObject.put("network_info", APL.getConnectivityManager().getActiveNetworkInfo()); } } catch (JSONException e) { Timber.e(e, "Exception converting to JSON object WiFiApConfig"); } return jsonObject; } public String toShortString() { StringBuilder sb = new StringBuilder(); sb.append(String.format("INTERNAL Id: %s, SSID: %s, RSSI: %d, LEVEL: %d, NETID: %d", getId().toString(), getSSID(), mRssi, getLevel(), getNetworkId())); sb.append(" - " + getProxyStatusString()); if (getStatus() != null) sb.append(" - " + getStatus().toShortString()); return sb.toString(); } public String getProxyStatusString() { ProxySetting setting = getProxySetting(); String result = ""; try { // APL.getTraceUtils().startTrace(TAG,"getProxyStatusString", Log.DEBUG); if (setting == null) { result = APL.getContext().getString(R.string.not_available); } else if (setting == ProxySetting.NONE || setting == ProxySetting.UNASSIGNED) { result = APL.getContext().getString(R.string.direct_connection); } else if (setting == ProxySetting.STATIC) { StringBuilder sb = new StringBuilder(); if (!TextUtils.isEmpty(proxyHost) && proxyPort != null && proxyPort > 0) sb.append(String.format("%s:%d", proxyHost, proxyPort)); else { sb.append(APL.getContext().getString(R.string.not_set)); } result = sb.toString(); } else if (setting == ProxySetting.PAC) { StringBuilder sb = new StringBuilder(); if (!TextUtils.isEmpty(pacFileUri.toString())) sb.append(String.format("%s", pacFileUri)); else { sb.append(APL.getContext().getString(R.string.not_set)); } result = sb.toString(); } else { result = APL.getContext().getString(R.string.not_valid_proxy_setting); } // APL.getTraceUtils().stopTrace(TAG, "getProxyStatusString", result, Log.DEBUG); } catch (Exception e) { Timber.e(e,"Exception building proxy status string"); } return result; } public String getProxyHostString() { return proxyHost; } public String getProxyIPHost() { return proxyHost; } public String getProxyHost() { return proxyHost; } public Integer getProxyPort() { return proxyPort; } public String getProxyExclusionList() { if (getStringProxyExclusionList() == null) return ""; else return getStringProxyExclusionList(); } public Uri getPacFileUri() { return Uri.parse(pacFileUri); } public void setPacUriFile(Uri pacUriFile) { this.pacFileUri = pacUriFile.toString(); } public CheckStatusValues getCheckingStatus() { return getStatus().getCheckingStatus(); } public String getBSSID() { return bssid; } public String getAPConnectionStatus() { if (isActive()) { return APL.getContext().getString(R.string.connected); } else if (getLevel() > 0) { return APL.getContext().getString(R.string.available); } else { return APL.getContext().getString(R.string.not_available); } } public static String removeDoubleQuotes(String string) { int length = string.length(); if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { return string.substring(1, length - 1); } return string; } public static String convertToQuotedString(String string) { return "\"" + string + "\""; } public int getLevel() { if (mRssi == Integer.MAX_VALUE) { return -1; } return WifiManager.calculateSignalLevel(mRssi, 4); } public void clearScanStatus() { mRssi = Integer.MAX_VALUE; pskType = PskType.UNKNOWN; } public UUID getId() { return id; } public APLNetworkId getAPLNetworkId() { return internalWifiNetworkId; } public ProxyStatus getStatus() { return status; } public String getStringProxyExclusionList() { return stringProxyExclusionList; } public String getSSID() { return ssid; } public SecurityType getSecurityType() { return securityType; } public int getNetworkId() { return networkId; } public PskType getPskType() { return pskType; } public WifiConfiguration getWifiConfig() { return wifiConfig; } public int getRssi() { return mRssi; } }