package org.droidplanner.services.android.impl.core.drone.autopilot.apm.solo; import android.content.Context; import android.os.Handler; import android.os.RemoteException; import android.text.TextUtils; import com.o3dr.android.client.utils.connection.AbstractIpConnection; import com.o3dr.android.client.utils.connection.IpConnectionListener; import com.o3dr.services.android.lib.model.ICommandListener; import org.droidplanner.services.android.impl.communication.model.DataLink; import org.droidplanner.services.android.impl.utils.connection.SshConnection; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import timber.log.Timber; /** * Created by Fredia Huya-Kouadio on 2/20/15. */ public abstract class AbstractLinkManager<T extends AbstractLinkManager.LinkListener> implements IpConnectionListener { protected static final String SOLO_MAC_ADDRESS_COMMAND = "/sbin/ifconfig wlan0 | awk '/HWaddr/ {print $NF}'"; protected static final long RECONNECT_COUNTDOWN = 1000l; //ms public abstract void refreshState(); public interface LinkListener { void onLinkConnected(); void onLinkDisconnected(); void onVersionsUpdated(); void onMacAddressUpdated(); } private final Runnable reconnectTask = new Runnable() { @Override public void run() { handler.removeCallbacks(reconnectTask); linkConn.connect(linkProvider.getConnectionExtras()); } }; private final Runnable macAddressRetriever = new Runnable() { @Override public void run() { try{ final String response = getSshLink().execute(SOLO_MAC_ADDRESS_COMMAND); final String trimmedResponse = TextUtils.isEmpty(response) ? "" : response.trim(); setMacAddress(trimmedResponse); } catch (IOException e) { Timber.e(e, "Error occurred while retrieving sololink mac address"); } } }; private final AtomicReference<String> macAddress = new AtomicReference<>(""); private final ExecutorService asyncExecutor; protected final Handler handler; private final AtomicBoolean isStarted = new AtomicBoolean(false); private final AtomicBoolean wasConnected = new AtomicBoolean(false); protected final Context context; protected final AbstractIpConnection linkConn; protected final DataLink.DataLinkProvider linkProvider; private T linkListener; public AbstractLinkManager(Context context, AbstractIpConnection ipConn, Handler handler, ExecutorService asyncExecutor, DataLink.DataLinkProvider linkProvider) { this.context = context; this.linkConn = ipConn; this.linkConn.setIpConnectionListener(this); this.handler = handler; this.asyncExecutor = asyncExecutor; this.linkProvider = linkProvider; } protected void postAsyncTask(Runnable task){ if(asyncExecutor != null && !asyncExecutor.isShutdown()){ asyncExecutor.execute(task); } } protected void postSuccessEvent(final ICommandListener listener){ if(handler != null && listener != null){ handler.post(new Runnable() { @Override public void run() { try { listener.onSuccess(); } catch (RemoteException e) { Timber.e(e, e.getMessage()); } } }); } } protected void postTimeoutEvent(final ICommandListener listener){ if(handler != null && listener != null){ handler.post(new Runnable() { @Override public void run() { try { listener.onTimeout(); } catch (RemoteException e) { Timber.e(e, e.getMessage()); } } }); } } protected void postErrorEvent(final int error, final ICommandListener listener){ if(handler != null && listener != null){ handler.post(new Runnable() { @Override public void run() { try { listener.onError(error); } catch (RemoteException e) { Timber.e(e, e.getMessage()); } } }); } } protected abstract SshConnection getSshLink(); public boolean isLinkConnected() { return this.linkConn.getConnectionStatus() == AbstractIpConnection.STATE_CONNECTED; } public void start(T listener) { handler.removeCallbacks(reconnectTask); isStarted.set(true); this.linkConn.connect(linkProvider.getConnectionExtras()); this.linkListener = listener; } public void stop() { handler.removeCallbacks(reconnectTask); isStarted.set(false); //Break the link this.linkConn.disconnect(); } @Override public void onIpConnected() { handler.removeCallbacks(reconnectTask); wasConnected.set(true); refreshState(); if (linkListener != null) linkListener.onLinkConnected(); } protected boolean isStarted(){ return isStarted.get(); } @Override public void onIpDisconnected() { if (isStarted.get()) { if(shouldReconnect()) { //Try to reconnect handler.postDelayed(reconnectTask, RECONNECT_COUNTDOWN); } } if (linkListener != null && wasConnected.compareAndSet(true, false)) linkListener.onLinkDisconnected(); } protected boolean shouldReconnect(){ return true; } protected void loadMacAddress(){ postAsyncTask(macAddressRetriever); } private void setMacAddress(String trimmedResponse) { Timber.i("Retrieved mac address: %s", trimmedResponse); macAddress.set(trimmedResponse); if(linkListener != null){ linkListener.onMacAddressUpdated(); } } public String getMacAddress(){ return macAddress.get(); } }