Android Open Source - fb-android-bluetooth Bluetooth Service






From Project

Back to project page fb-android-bluetooth.

License

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.

Java Source Code

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();
        }
    }
}




Java Source Code List

com.fizzbuzz.android.bluetooth.BluetoothDiscoveryBroadcastReceiver.java
com.fizzbuzz.android.bluetooth.BluetoothService.java
com.fizzbuzz.android.bluetooth.LoggingManager.java