package pl.llp.aircasting.sensor;
import pl.llp.aircasting.android.Logger;
import pl.llp.aircasting.event.ConnectionUnsuccessfulEvent;
import pl.llp.aircasting.util.Constants;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import com.google.common.eventbus.EventBus;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Created by ags on 26/09/12 at 15:58
*/
public class BluetoothConnector
{
public static final UUID SPP_SERIAL = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private final BluetoothAdapter adapter;
private final BluetoothDevice device;
private final EventBus eventBus;
private final AtomicBoolean active = new AtomicBoolean(false);
private static final long MAX_CONNECTION_FAILURE_TIME = Constants.ONE_MINUTE;
private volatile long connectionFailingSince = -1;
private boolean noSuchMethod;
public BluetoothConnector(BluetoothAdapter adapter, BluetoothDevice device, EventBus eventBus)
{
this.adapter = adapter;
this.device = device;
this.eventBus = eventBus;
}
public BluetoothSocket getSocket()
{
active.set(true);
BluetoothSocket socket = null;
cancelDiscovery();
while (socket == null && active.get())
{
try
{
try
{
socket = getSocketByHack(1);
}
catch (NoSuchMethodException e)
{
socket = getSocketNormally();
}
}
catch (Exception e)
{
considerStoppingOnFailure();
Logger.w("Couldn't connect to device [" + device.getName() + ", " + device.getAddress() + "]", e);
sleepFor(Constants.THREE_SECONDS);
socket = null;
}
}
return socket;
}
public static void sleepFor(long sleepTime)
{
try
{
Thread.sleep(sleepTime);
}
catch (InterruptedException ignore)
{
}
}
public BluetoothSocket getSocketNormally() throws IOException
{
return device.createRfcommSocketToServiceRecord(SPP_SERIAL);
}
private BluetoothSocket getSocketByHack(int channel) throws Exception
{
if(noSuchMethod)
{
return null;
}
try
{
Method m = device.getClass().getMethod("createRfcommSocket", int.class);
return (BluetoothSocket) m.invoke(device, channel);
}
catch (NoSuchMethodException e)
{
noSuchMethod = true;
throw e;
}
}
private void considerStoppingOnFailure()
{
long currentTime = System.currentTimeMillis();
if(connectionFailingSince < 0)
{
connectionFailingSince = currentTime;
}
else
{
long difference = currentTime - connectionFailingSince;
if(difference > MAX_CONNECTION_FAILURE_TIME)
{
eventBus.post(new ConnectionUnsuccessfulEvent(device));
stop();
}
}
}
public BluetoothSocket connect(BluetoothSocket socket)
{
cancelDiscovery();
active.set(true);
while (active.get())
{
try
{
socket.connect();
return socket;
}
catch (Exception e)
{
considerStoppingOnFailure();
Logger.w("Couldn't connect to device [" + device.getName() + ", " + device.getAddress() + "]", e);
sleepFor(Constants.THREE_SECONDS);
}
}
return null;
}
public void stop()
{
active.set(false);
}
private void cancelDiscovery()
{
synchronized (adapter)
{
if(adapter.isDiscovering())
{
adapter.cancelDiscovery();
}
}
}
}