/* * Copyright (c) 2017. Mathias Ciliberto, Francisco Javier OrdoƱez Morales, * Hristijan Gjoreski, Daniel Roggen * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the * Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package uk.ac.sussex.wear.android.datalogger.bt; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * Created by fjordonez on 27/01/17. */ public class BluetoothConnector { private BluetoothSocketWrapper bluetoothSocket; private BluetoothDevice device; private boolean secure; private BluetoothAdapter adapter; private List<UUID> uuidCandidates; private int candidate; /** * @param device the device * @param secure if connection should be done via a secure socket * @param adapter the Android BT adapter * @param uuid a list of UUIDs. if null or empty, the Serial PP id is used */ public BluetoothConnector(BluetoothDevice device, boolean secure, BluetoothAdapter adapter, UUID uuid) { this.device = device; this.secure = secure; this.adapter = adapter; this.uuidCandidates = new ArrayList<UUID>(); this.uuidCandidates.add(uuid); if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) { // TODO } } public BluetoothSocketWrapper connect() throws IOException { boolean success = false; while (selectSocket()) { adapter.cancelDiscovery(); try { bluetoothSocket.connect(); success = true; break; } catch (IOException e) { //try the fallback try { bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket()); Thread.sleep(500); bluetoothSocket.connect(); success = true; break; } catch (FallbackException e1) { Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e); } catch (InterruptedException e1) { Log.w("BT", e1.getMessage(), e1); } catch (IOException e1) { Log.w("BT", "Fallback failed. Cancelling.", e1); } } } if (!success) { throw new IOException("===> Could not connect to device: " + device.getAddress()); } return bluetoothSocket; } private boolean selectSocket() throws IOException { if (candidate >= uuidCandidates.size()) { return false; } BluetoothSocket tmp; UUID uuid = uuidCandidates.get(candidate++); Log.e("BT", "===> Attempting to connect to Protocol: " + uuid); if (secure) { tmp = device.createRfcommSocketToServiceRecord(uuid); } else { tmp = device.createInsecureRfcommSocketToServiceRecord(uuid); } bluetoothSocket = new NativeBluetoothSocket(tmp); return true; } public void close() { if (bluetoothSocket != null) { try { bluetoothSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } public static interface BluetoothSocketWrapper { InputStream getInputStream() throws IOException; OutputStream getOutputStream() throws IOException; String getRemoteDeviceName(); void connect() throws IOException; String getRemoteDeviceAddress(); void close() throws IOException; BluetoothSocket getUnderlyingSocket(); } public static class NativeBluetoothSocket implements BluetoothSocketWrapper { private BluetoothSocket socket; public NativeBluetoothSocket(BluetoothSocket tmp) { this.socket = tmp; } @Override public InputStream getInputStream() throws IOException { return socket.getInputStream(); } @Override public OutputStream getOutputStream() throws IOException { return socket.getOutputStream(); } @Override public String getRemoteDeviceName() { return socket.getRemoteDevice().getName(); } @Override public void connect() throws IOException { socket.connect(); } @Override public String getRemoteDeviceAddress() { return socket.getRemoteDevice().getAddress(); } @Override public void close() throws IOException { socket.close(); } @Override public BluetoothSocket getUnderlyingSocket() { return socket; } } public class FallbackBluetoothSocket extends NativeBluetoothSocket { private BluetoothSocket fallbackSocket; public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException { super(tmp); try { Class<?> clazz = tmp.getRemoteDevice().getClass(); Class<?>[] paramTypes = new Class<?>[]{Integer.TYPE}; Method m = clazz.getMethod("createRfcommSocket", paramTypes); Object[] params = new Object[]{Integer.valueOf(1)}; fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params); } catch (Exception e) { throw new FallbackException(e); } } @Override public InputStream getInputStream() throws IOException { return fallbackSocket.getInputStream(); } @Override public OutputStream getOutputStream() throws IOException { return fallbackSocket.getOutputStream(); } @Override public void connect() throws IOException { fallbackSocket.connect(); } @Override public void close() throws IOException { fallbackSocket.close(); } } public static class FallbackException extends Exception { /** * */ private static final long serialVersionUID = 1L; public FallbackException(Exception e) { super(e); } } }