Back to project page fb-android-bluetooth.
The source code is released under:
Apache License
If you think the Android project fb-android-bluetooth listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.fizzbuzz.android.bluetooth; //ww w . ja v a 2 s.c om import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Intent; import android.os.IBinder; import com.fizzbuzz.android.dagger.InjectingApplication; import com.fizzbuzz.android.dagger.InjectingService; import dagger.Module; import dagger.Provides; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.List; import java.util.UUID; /** * A base helper class for implementing bluetooth support on Android. Supports making outbound connections and * listening for inbound connections. Subclasses should provide an overriding implementation of * {@link #runWhileConnected(android.bluetooth.BluetoothSocket)} */ public class BluetoothService extends InjectingService { private enum State { STATE_NONE, STATE_LISTENING, STATE_CONNECTING, STATE_CONNECTED } public static final String INTENT_ACTION_LISTEN_FOR_BLUETOOTH_CONNECTIONS = "com.fizzbuzz.android.bluetooth" + ".LISTEN_FOR_BLUETOOTH_CONNECTIONS"; public static final String INTENT_ACTION_CONNECT_TO_BLUETOOTH_DEVICE = "com.fizzbuzz.android.bluetooth" + ".CONNECT_TO_BLUETOOTH_DEVICE"; // https://www.bluetooth.org/en-us/specification/assigned-numbers-overview/service-discovery private static UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); private final Logger mLogger = LoggerFactory.getLogger(LoggingManager.TAG); @Inject BluetoothAdapter mBtAdapter; private State mState; private ListeningThread mListeningThread; private ConnectingThread mConnectingThread; private ConnectedThread mConnectedThread; @Override public void onCreate() { super.onCreate(); ((InjectingApplication) getApplicationContext()).getObjectGraph().inject(this); //TODO: call startForeground } @Override protected List<Object> getModules() { return Arrays.<Object>asList(new BluetoothSeviceModule()); } @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); // if the intent is null, it means the service was restarted after being killed by the system if (intent.getAction().equals(INTENT_ACTION_LISTEN_FOR_BLUETOOTH_CONNECTIONS)) { reset(); listen(); } else if (intent.getAction().equals(INTENT_ACTION_CONNECT_TO_BLUETOOTH_DEVICE)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); reset(); connect(device); } // if the system kills the service to reclaim resources, it should restart it later, // resending the intent. // see http://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT return START_REDELIVER_INTENT; } @Override public void onDestroy() { super.onDestroy(); stop(); } @Override public IBinder onBind(Intent intent) { return null; // binding support not implemented } /** * Spawns a thread to connect to a bluetooth device. If there is another thread in the process of making a * connection, or a thread already connected to a device, those threads are cancelled first. * * @param device the target device */ public synchronized void connect(BluetoothDevice device) { // if a thread is already in the process of connecting to another device, cancel that other one if (getServiceState() == State.STATE_CONNECTING) { if (mConnectingThread != null) { mLogger.debug("BluetoothService.connect: canceling previous connecting thread"); mConnectingThread.cancel(); mConnectingThread = null; } } // if we're already connected to another device, disconnect and end that thread if (mConnectedThread != null) { mLogger.debug("BluetoothService.connect: canceling previous connected thread"); mConnectedThread.cancel(); // this will close the socket, which will cause run() to exit mConnectedThread = null; } setServiceState(State.STATE_CONNECTING); mLogger.debug("BluetoothService.connect: starting new connecting thread"); mConnectingThread = new ConnectingThread(device); mConnectingThread.start(); } protected synchronized void onConnectionFailed(final BluetoothDevice device) { reset(); } protected void onInboundConnectionRequestReceived(final BluetoothDevice device) { } protected void onBeforeConnect(final BluetoothDevice device) { } protected synchronized void onConnected(BluetoothSocket socket) { setServiceState(State.STATE_CONNECTED); mLogger.debug("BluetoothService.onConnected: starting new connected thread"); mConnectedThread = new ConnectedThread(socket); mConnectedThread.start(); } /** * Invoked once a connection has been established with a device. Subclasses should override to implement * the logic they want to execute while connected. * * @param socket the connected bluetooth socket */ protected void runWhileConnected(final BluetoothSocket socket) { // invoked by ConnectedThread.run(). To be implemented by subclass. } private synchronized State getServiceState() { return mState; } private synchronized void setServiceState(State state) { mLogger.debug("BluetoothService.setServiceState: old state = {}, new state = {}", mState, state); mState = state; } private synchronized void reset() { // if there's a thread that's currently connecting to a device, cancel it if (mConnectingThread != null) { mLogger.debug("BluetoothService.reset: canceling previous connecting thread"); mConnectingThread.cancel(); mConnectingThread = null; } // if we're already connected to another device, disconnect and end that thread if (mConnectedThread != null) { mLogger.debug("BluetoothService.reset: canceling previous connected thread"); mConnectedThread.cancel(); // this will close the socket, which will cause run() to exit mConnectedThread = null; } setServiceState(State.STATE_NONE); } private synchronized void listen() { setServiceState(State.STATE_LISTENING); if (mListeningThread == null) { mLogger.info("BluetoothService.listen: starting listening thread"); mListeningThread = new ListeningThread("Bluetooth Inspector", SPP_UUID); mListeningThread.start(); } } private synchronized void onInboundConnection(BluetoothSocket socket) { // cancel the listening thread, because we only want to connect to one device at a time mListeningThread.cancel(); setServiceState(State.STATE_CONNECTED); } private synchronized void stop() { if (mListeningThread != null) { mListeningThread.cancel(); mListeningThread = null; } setServiceState(State.STATE_NONE); } @Module(injects = {BluetoothService.class}) public static class BluetoothSeviceModule { public BluetoothSeviceModule() { } @Provides @Singleton public BluetoothAdapter provideBluetoothAdapter() { return BluetoothAdapter.getDefaultAdapter(); } } private class ListeningThread extends Thread { private final UUID mHostUUID; private final String mHostName; private BluetoothServerSocket mServerSocket; private ListeningThread(String hostName, UUID hostUUID) { mHostName = hostName; mHostUUID = hostUUID; } @Override public void run() { setName(mHostName); // establish the server socket try { mLogger.debug("BluetoothService$ListeningThread.run: opening server socket"); mServerSocket = mBtAdapter.listenUsingRfcommWithServiceRecord(mHostName, mHostUUID); } catch (IOException e) { mLogger.error("BluetoothService$ListeningThread.run: caught IOException from " + "listenUsingRfcommWithServiceRecord() call", e); return; } BluetoothSocket socket = null; while (getServiceState() == BluetoothService.State.STATE_LISTENING) { try { // listen on the server socket. Keep in mind that the state can change while we're waiting. // Note that when this call returns a BluetoothSocket, the socket is _already_ connected. mLogger.debug("BluetoothService$ListeningThread.run: calling accept()"); socket = mServerSocket.accept(); // blocking call mLogger.debug("BluetoothService$ListeningThread.run: accepted remote connection, " + "current state = " + getServiceState().toString()); onInboundConnectionRequestReceived(socket.getRemoteDevice()); } catch (IOException e) { mLogger.error("BluetoothService$ListeningThread.run: caught IOException from server socket " + "accept() call", e); break; } if (getServiceState() == BluetoothService.State.STATE_LISTENING) { onInboundConnection(socket); } else { mLogger.debug("BluetoothService$ListeningThread.run: current state is not " + "STATE_LISTENING, so closing socket"); try { socket.close(); } catch (IOException e) { mLogger.error("BluetoothService$ListeningThread.run: caught IOException from " + "socket close() call", e); break; } } } mLogger.debug("BluetoothService$ListeningThread.run: exiting"); cancel(); } private synchronized void cancel() { try { if (mServerSocket != null) { mLogger.debug("BluetoothService$ListentingThread.cancel: closing server socket"); mServerSocket.close(); mServerSocket = null; } } catch (IOException e) { mLogger.error("BluetoothService$ListeningThread.cancel: caught IOException when closing server " + "socket", e); } } } private class ConnectingThread extends Thread { private final BluetoothDevice mDevice; private BluetoothSocket mSocket; public ConnectingThread(BluetoothDevice device) { mDevice = device; } public synchronized void cancel() { try { if (mSocket != null) { mLogger.debug("BluetoothService$ConnectingThread.cancel: closing socket"); mSocket.close(); mSocket = null; } } catch (IOException e) { mLogger.error("BluetoothService$ConnectingThread.cancel: caught IOException when closing " + "socket", e); } } @Override public void run() { onBeforeConnect(mDevice); try { mLogger.debug("BluetoothService$ConnectingThread.run: opening socket"); mSocket = mDevice.createRfcommSocketToServiceRecord(SPP_UUID); } catch (IOException e) { mLogger.error("BluetoothService$ConnectingThread.run: caught IOException when creating socket", e); onConnectionFailed(mDevice); return; } try { mLogger.debug("BluetoothService$ConnectingThread.run: connecting to device"); mSocket.connect(); mLogger.debug("BluetoothService$ConnectingThread.run: connected successfully"); onConnected(mSocket); } catch (IOException e) { mLogger.error("BluetoothService$ConnectingThread.run: caught IOException when connecting to " + "socket", e); try { mSocket.close(); } catch (IOException e2) { mLogger.error("BluetoothService$ConnectingThread.run: caught IOException when closing " + "socket", e2); } onConnectionFailed(mDevice); } mLogger.debug("BluetoothService$ConnectingThread.run: exiting"); } } private class ConnectedThread extends Thread { private BluetoothSocket mSocket; private InputStream mInStream; private OutputStream mOutStream; public ConnectedThread(BluetoothSocket socket) { mSocket = socket; } public BluetoothSocket getSocket() { return mSocket; } public synchronized void cancel() { try { if (mSocket != null) { mLogger.debug("BluetoothService$ConnectedThread.cancel: closing socket"); mSocket.close(); mSocket = null; } } catch (IOException e) { mLogger.error("BluetoothService$ConnectedThread.cancel: caught IOException when closing socket", e); } } @Override public void run() { runWhileConnected(mSocket); // implementation supplied by BluetoothService subclass cancel(); } } }