package org.droidplanner.services.android.impl.communication.connection;
import android.content.Context;
import android.net.Uri;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.text.TextUtils;
import com.o3dr.services.android.lib.drone.connection.ConnectionParameter;
import com.o3dr.services.android.lib.drone.connection.ConnectionType;
import com.o3dr.services.android.lib.gcs.link.LinkConnectionStatus;
import org.droidplanner.services.android.impl.utils.connection.WifiConnectionHandler;
import java.io.IOException;
import java.sql.Connection;
import java.util.List;
import timber.log.Timber;
/**
* Abstract the connection to a Solo vehicle.
* Created by Fredia Huya-Kouadio on 12/17/15.
*/
public class SoloConnection extends AndroidMavLinkConnection implements WifiConnectionHandler.WifiConnectionListener {
private static final int SOLO_UDP_PORT = 14550;
private final WifiConnectionHandler wifiHandler;
private final AndroidUdpConnection dataLink;
private final String soloLinkId;
private final String soloLinkPassword;
public SoloConnection(Context applicationContext, String soloLinkId, String password) {
super(applicationContext);
this.wifiHandler = new WifiConnectionHandler(applicationContext);
wifiHandler.setListener(this);
this.soloLinkId = soloLinkId;
this.soloLinkPassword = password;
this.dataLink = new AndroidUdpConnection(applicationContext, SOLO_UDP_PORT) {
@Override
protected void onConnectionOpened(Bundle extras) {
SoloConnection.this.onConnectionOpened(extras);
}
@Override
protected void onConnectionStatus(LinkConnectionStatus connectionStatus) {
SoloConnection.this.onConnectionStatus(connectionStatus);
}
};
}
@Override
protected void openConnection(Bundle connectionExtras) throws IOException {
if (TextUtils.isEmpty(soloLinkId)) {
LinkConnectionStatus connectionStatus = LinkConnectionStatus
.newFailedConnectionStatus(LinkConnectionStatus.INVALID_CREDENTIALS, "Invalid connection credentials!");
onConnectionStatus(connectionStatus);
} else {
wifiHandler.start();
checkScanResults(wifiHandler.getScanResults());
}
}
private void refreshWifiAps() {
if (!wifiHandler.refreshWifiAPs()) {
LinkConnectionStatus connectionStatus = LinkConnectionStatus
.newFailedConnectionStatus(LinkConnectionStatus.SYSTEM_UNAVAILABLE, "Unable to refresh wifi access points");
onConnectionStatus(connectionStatus);
}
}
@Override
protected int readDataBlock(byte[] buffer) throws IOException {
return dataLink.readDataBlock(buffer);
}
@Override
protected void sendBuffer(byte[] buffer) throws IOException {
dataLink.sendBuffer(buffer);
}
@Override
protected void closeConnection() throws IOException {
wifiHandler.stop();
dataLink.closeConnection();
}
@Override
protected void loadPreferences() {
dataLink.loadPreferences();
}
@Override
public int getConnectionType() {
return dataLink.getConnectionType();
}
@Override
public void onWifiConnected(String wifiSsid, Bundle extras) {
if (isConnecting()) {
//Let's see if we're connected to our target wifi
if (wifiSsid.equalsIgnoreCase(soloLinkId)) {
//We're good to go
try {
dataLink.openConnection(extras);
} catch (IOException e) {
reportIOException(e);
Timber.e(e, e.getMessage());
}
}
}
}
@Override
public void onWifiConnecting() {
onConnectionStatus(new LinkConnectionStatus(LinkConnectionStatus.CONNECTING, null));
}
@Override
public void onWifiDisconnected(String prevSsid) {
if (prevSsid.equalsIgnoreCase(soloLinkId)) {
onConnectionStatus(new LinkConnectionStatus(LinkConnectionStatus.DISCONNECTED, null));
}
}
@Override
public void onWifiScanResultsAvailable(List<ScanResult> results) {
checkScanResults(results);
}
@Override
public void onWifiConnectionFailed(LinkConnectionStatus connectionStatus) {
onConnectionStatus(connectionStatus);
}
private void checkScanResults(List<ScanResult> results) {
if (!isConnecting())
return;
//We're in the connection process, let's see if the wifi we want is available
ScanResult targetResult = null;
for (ScanResult result : results) {
if (result.SSID.equalsIgnoreCase(this.soloLinkId)) {
//bingo
targetResult = result;
break;
}
}
if (targetResult != null) {
//We're good to go
try {
Bundle connectInfo = new Bundle();
Bundle extras = getConnectionExtras();
if (extras != null && !extras.isEmpty()) {
connectInfo.putAll(extras);
}
connectInfo.putParcelable(WifiConnectionHandler.EXTRA_SCAN_RESULT, targetResult);
connectInfo.putString(WifiConnectionHandler.EXTRA_SSID_PASSWORD, soloLinkPassword);
int connectionResult = wifiHandler.connectToWifi(connectInfo);
if (connectionResult != 0) {
@LinkConnectionStatus.FailureCode int failureCode = connectionResult;
LinkConnectionStatus connectionStatus = LinkConnectionStatus
.newFailedConnectionStatus(failureCode, "Unable to connect to the target wifi " + soloLinkId);
onConnectionStatus(connectionStatus);
}
} catch (IllegalArgumentException e) {
Timber.e(e, e.getMessage());
LinkConnectionStatus connectionStatus = LinkConnectionStatus.newFailedConnectionStatus(LinkConnectionStatus.UNKNOWN, e.getMessage());
onConnectionStatus(connectionStatus);
}
} else {
//Let's try again
refreshWifiAps();
}
}
private boolean isConnecting() {
return getConnectionStatus() == MAVLINK_CONNECTING;
}
public static boolean isUdpSoloConnection(Context context, ConnectionParameter connParam){
if(connParam == null)
return false;
final int connectionType = connParam.getConnectionType();
switch(connectionType){
case ConnectionType.TYPE_UDP:
Bundle paramsBundle = connParam.getParamsBundle();
if(paramsBundle == null)
return false;
final int serverPort = paramsBundle.getInt(ConnectionType.EXTRA_UDP_SERVER_PORT, ConnectionType.DEFAULT_UDP_SERVER_PORT);
final String wifiSsid = WifiConnectionHandler.getCurrentWifiLink((WifiManager) context.getSystemService(Context.WIFI_SERVICE));
return WifiConnectionHandler.isSoloWifi(wifiSsid) && serverPort == SOLO_UDP_PORT;
default:
return false;
}
}
public static ConnectionParameter getSoloConnectionParameterFromUdp(Context context, ConnectionParameter udpConnectionParameters){
if(context == null)
return null;
final String wifiSsid = WifiConnectionHandler.getCurrentWifiLink((WifiManager) context.getSystemService(Context.WIFI_SERVICE));
if(WifiConnectionHandler.isSoloWifi(wifiSsid)){
return ConnectionParameter.newSoloConnection(wifiSsid, null, udpConnectionParameters.getTLogLoggingUri(), udpConnectionParameters.getEventsDispatchingPeriod());
}
return null;
}
}