package eu.hgross.blaubot.android.bluetooth; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import java.io.IOException; import java.util.Arrays; import java.util.List; import eu.hgross.blaubot.core.BlaubotConstants; import eu.hgross.blaubot.core.IBlaubotAdapter; import eu.hgross.blaubot.core.IBlaubotConnection; import eu.hgross.blaubot.core.IBlaubotDevice; import eu.hgross.blaubot.core.acceptor.ConnectionMetaDataDTO; import eu.hgross.blaubot.core.acceptor.IBlaubotIncomingConnectionListener; import eu.hgross.blaubot.core.acceptor.UniqueDeviceIdHelper; import eu.hgross.blaubot.core.acceptor.discovery.BeaconMessage; import eu.hgross.blaubot.core.acceptor.discovery.IBlaubotBeaconStore; import eu.hgross.blaubot.core.connector.IBlaubotConnector; import eu.hgross.blaubot.core.connector.IncompatibleBlaubotDeviceException; import eu.hgross.blaubot.core.statemachine.BlaubotAdapterHelper; import eu.hgross.blaubot.util.Log; /** * Bluetooth connector implementation for android. * * @author Henning Gross {@literal (mail.to@henning-gross.de)} * */ public class BlaubotBluetoothConnector implements IBlaubotConnector { private static final String LOG_TAG = "BlaubotBluetoothConnector"; private static final List<String> SUPPORTED_ACCEPTOR_TYPES = Arrays.asList(BlaubotConstants.ACCEPTOR_TYPE_RFCOMM_ANDROID_BLUETOOTH, BlaubotConstants.ACCEPTOR_TYPE_RFCOMM_JSR82_BLUETOOTH); private final IBlaubotDevice ownDevice; private IBlaubotIncomingConnectionListener incomingConnectionListener; private BlaubotBluetoothAdapter blaubotBluetoothAdapter; private IBlaubotBeaconStore beaconStore; public BlaubotBluetoothConnector(BlaubotBluetoothAdapter blaubotBluetoothAdapter, IBlaubotDevice ownDevice) { this.ownDevice = ownDevice; this.blaubotBluetoothAdapter = blaubotBluetoothAdapter; } @Override public List<String> getSupportedAcceptorTypes() { return SUPPORTED_ACCEPTOR_TYPES; } @Override public IBlaubotAdapter getAdapter() { return blaubotBluetoothAdapter; } @Override public void setBeaconStore(IBlaubotBeaconStore beaconStore) { this.beaconStore = beaconStore; } @Override public void setIncomingConnectionListener(IBlaubotIncomingConnectionListener incomingConnectionListener) { this.incomingConnectionListener = incomingConnectionListener; } @Override public IBlaubotConnection connectToBlaubotDevice(IBlaubotDevice blaubotDevice) { final String uniqueDeviceID = blaubotDevice.getUniqueDeviceID(); // check if we have meta data final List<ConnectionMetaDataDTO> lastKnownConnectionMetaData = beaconStore.getLastKnownConnectionMetaData(uniqueDeviceID); if(lastKnownConnectionMetaData == null) { if(Log.logErrorMessages()) { Log.e(LOG_TAG, "Could not get connection meta data for unique device id " + uniqueDeviceID); } return null; } // take the first supported acceptor, if any final List<ConnectionMetaDataDTO> supportedAcceptors = BlaubotAdapterHelper.filterBySupportedAcceptorTypes(lastKnownConnectionMetaData, getSupportedAcceptorTypes()); if(supportedAcceptors.isEmpty()) { throw new IncompatibleBlaubotDeviceException(blaubotDevice + " could not get acceptor meta data for this device."); } BluetoothConnectionMetaDataDTO bluetoothConnectionMetaData = new BluetoothConnectionMetaDataDTO(supportedAcceptors.get(0)); final String bluetoothMacAddress = bluetoothConnectionMetaData.getMacAddress(); // TODO: we want to decouple the android device form our IBlaubotDevice and create a bluetooth device here based on the mac // we want to do smthg like this here: final BluetoothAdapter androidBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); final BluetoothDevice remoteDevice = androidBluetoothAdapter.getRemoteDevice(bluetoothMacAddress); BluetoothSocket socket = null; try { BlaubotConstants.BLUETOOTH_ADAPTER_LOCK.acquire(); try { socket = remoteDevice.createRfcommSocketToServiceRecord(blaubotBluetoothAdapter.getUUIDSet().getAppUUID()); socket.connect(); if(Log.logDebugMessages()) { Log.d(LOG_TAG, "Got a new connection from " + socket.getRemoteDevice()); } // send our unique device id UniqueDeviceIdHelper.sendUniqueDeviceIdThroughOutputStream(ownDevice, socket.getOutputStream()); // create the connection object BlaubotBluetoothDevice device = new BlaubotBluetoothDevice(blaubotDevice.getUniqueDeviceID(), socket.getRemoteDevice()); IBlaubotConnection connection = new BlaubotBluetoothConnection(device, socket); // send our acceptor data final BeaconMessage currentBeaconMessage = getAdapter().getBlaubot().getConnectionStateMachine().getBeaconService().getCurrentBeaconMessage(); connection.write(currentBeaconMessage.toBytes()); this.incomingConnectionListener.onConnectionEstablished(connection); return connection; } catch (IOException e) { if(Log.logWarningMessages()) { Log.w(LOG_TAG, "Bluetooth connect failed! Adding " + blaubotDevice + " (" + remoteDevice+ ") to dead devices. Error: " + e.getMessage(), e); } // TODO: maybe retry 1-2 times ?? if (socket != null) { try { socket.close(); } catch (IOException e1) { } } } catch (Exception e) { if(Log.logErrorMessages()) { Log.e(LOG_TAG, "Reflection failure or something worse.", e); } } finally { BlaubotConstants.BLUETOOTH_ADAPTER_LOCK.release(); } } catch (InterruptedException e) { e.printStackTrace(); } return null; } }