package uk.org.smithfamily.mslogger.comms;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import uk.org.smithfamily.mslogger.MSLoggerApplication;
import uk.org.smithfamily.mslogger.log.DebugLogManager;
import android.os.*;
import android.util.Log;
/**
* Main connection class that wrap all the Bluetooth stuff for communications
*/
abstract class ConnectionManager
{
protected Connection conn;
protected InputStream mmInStream;
protected OutputStream mmOutStream;
protected Handler handler;
protected static final long IO_TIMEOUT = 500;
public enum ConnectionState
{
STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED
};
protected volatile ConnectionState currentState = ConnectionState.STATE_DISCONNECTED;
/**
* Get the instance name
*
* @return
*/
public String getInstanceName()
{
return "ConnectionManager";
}
/**
* Get the current Bluetooth connection state
*
* @return
*/
public ConnectionState getCurrentState()
{
return currentState;
}
/**
* Get the handler
*
* @return
*/
public Handler getHandler()
{
return handler;
}
/**
* Set the handler
*
* @param handler
*/
public void setHandler(final Handler handler)
{
this.handler = handler;
}
/**
* Initialise the Bluetooth connection
*
* @param handler
*
* @param btAddr
* @param adapter
* @param h
*/
public synchronized void init(final Handler handler, final String addr)
{
this.handler = handler;
if (conn == null)
{
conn = ConnectionFactory.INSTANCE.getConnection();
}
conn.init(addr);
if ((currentState != ConnectionState.STATE_DISCONNECTED) && !conn.isInitialised())
{
tearDown();
setState(ConnectionState.STATE_DISCONNECTED);
}
}
/**
* Set the current state of the Bluetooth connection
*
* @param state
*/
private void setState(final ConnectionState state)
{
currentState = state;
DebugLogManager.INSTANCE.log(getInstanceName() + ".setState " + state, Log.DEBUG);
}
/**
* Connect to the Bluetooth device
*
*
* @throws IOException
*/
public synchronized void connect() throws IOException
{
DebugLogManager.INSTANCE.log(getInstanceName() + ".connect() : Current state " + currentState, Log.DEBUG);
if (currentState != ConnectionState.STATE_DISCONNECTED)
{
return;
}
setState(ConnectionState.STATE_CONNECTING);
try
{
DebugLogManager.INSTANCE.log(getInstanceName() + ".connect() : Attempting connection", Log.DEBUG);
conn.connect();
}
catch (final IOException e)
{
DebugLogManager.INSTANCE.logException(e);
delay(1000);
try
{
conn.disconnect();
}
catch (final Exception e1)
{
DebugLogManager.INSTANCE.logException(e1);
}
conn.switchSettings();
try
{
conn.connect();
}
catch (final IOException e1)
{
// that didn't work, switch back and throw
DebugLogManager.INSTANCE.logException(e1);
conn.switchSettings();
throw e1;
}
}
InputStream tmpIn = null;
OutputStream tmpOut = null;
DebugLogManager.INSTANCE.log(getInstanceName() + ".connect() : Establishing connection", Log.DEBUG);
// Get the BluetoothSocket input and output streams
try
{
tmpIn = conn.getInputStream();
tmpOut = conn.getOutputStream();
}
catch (final IOException e)
{
DebugLogManager.INSTANCE.logException(e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
if ((mmInStream != null) && (mmOutStream != null))
{
setState(ConnectionState.STATE_CONNECTED);
DebugLogManager.INSTANCE.log(getInstanceName() + ".connect() : Current state " + currentState, Log.DEBUG);
}
else
{
DebugLogManager.INSTANCE.log(getInstanceName() + " Failed to complete connection", Log.ERROR);
setState(ConnectionState.STATE_DISCONNECTED);
tearDown();
}
return;
}
/**
* Check if connected and automatically connect if not
*
* @throws IOException
*/
protected synchronized void checkConnection() throws IOException
{
if (currentState == ConnectionState.STATE_DISCONNECTED)
{
connect();
}
}
/**
* Make the connection thread sleep
*
* @param d Delay
*/
protected void delay(final int d)
{
try
{
Thread.sleep(d);
}
catch (final InterruptedException e)
{
DebugLogManager.INSTANCE.log(getInstanceName() + " Sleep was interrupted", Log.ERROR);
}
}
/**
* Close input and output Bluetooth streams and socket
*/
public void tearDown()
{
if (conn != null)
{
conn.tearDown();
conn = null;
}
setState(ConnectionState.STATE_DISCONNECTED);
}
/**
* Write data to the Bluetooth stream
*
* @param data Data byte[] to send
* @throws IOException
*/
public synchronized void writeData(final byte[] data) throws IOException
{
checkConnection();
if (!conn.isConnected())
{
throw new IOException("Not connected");
}
DebugLogManager.INSTANCE.log(getInstanceName() + ".writeData ", data, Log.VERBOSE);
this.mmOutStream.write(data);
this.mmOutStream.flush();
}
/**
* Write data to the Bluetooth stream and return the result
*
* @param data Data byte[] to send
* @return
* @throws IOException
*/
public byte[] writeAndRead(final byte[] data) throws IOException
{
checkConnection();
writeData(data);
return readBytes();
}
/**
* Read bytes available on Bluetooth stream and return the resulting array
*
* @return Array of bytes read from Bluetooth stream
* @throws IOException
*/
public byte[] readBytes() throws IOException
{
final List<Byte> read = new ArrayList<Byte>();
synchronized (this)
{
while (mmInStream.available() > 0)
{
final byte b = (byte) mmInStream.read();
read.add(b);
}
}
final byte[] result = new byte[read.size()];
int i = 0;
for (final Byte b : read)
{
result[i++] = b;
}
DebugLogManager.INSTANCE.log(getInstanceName() + ".readBytes", result, Log.VERBOSE);
return result;
}
/**
* Flush all data on the Bluetooth stream by reading until nothing is available to read
*
* @throws IOException
*/
public synchronized void flushAll() throws IOException
{
DebugLogManager.INSTANCE.log(getInstanceName() + ".flushAll()", Log.DEBUG);
checkConnection();
mmOutStream.flush();
while (mmInStream.available() > 0)
{
mmInStream.read();
}
}
/**
* Send status to the application that appear in the status bar
*
* @param msgStr Message to be broadcasted
*/
public void sendStatus(final String msgStr)
{
DebugLogManager.INSTANCE.log(getInstanceName() + ".sendStatus " + msgStr, Log.DEBUG);
if (handler != null)
{
final Message msg = handler.obtainMessage(MSLoggerApplication.MESSAGE_TOAST);
final Bundle bundle = new Bundle();
bundle.putString(MSLoggerApplication.MSG_ID, msgStr);
msg.setData(bundle);
handler.sendMessage(msg);
}
}
/**
* Disconnect the current Bluetooth connection
*/
public synchronized void disconnect()
{
DebugLogManager.INSTANCE.log(getInstanceName() + ".disconnect()", Log.DEBUG);
tearDown();
}
}