Android Open Source - latrobe-datacapture-dir Bluetooth Connectivity Service






From Project

Back to project page latrobe-datacapture-dir.

License

The source code is released under:

MIT License

If you think the Android project latrobe-datacapture-dir 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.example.DataCaptureApp.services;
/* w w w . ja  v  a  2 s. c  o  m*/
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import com.example.DataCaptureApp.utils.ByteUtils;
import com.example.DataCaptureApp.data.DataService;
import com.example.DataCaptureApp.data.FailedInitialisationException;
import com.example.DataCaptureApp.utils.SerialisationUtils;
import com.example.DataCaptureApp.data.*;
import com.example.DataCaptureApp.transforms.DeserialiseDataTransform;

import java.io.IOException;
import java.util.Set;
import java.util.UUID;

/**
 * Created by Tom on 6/09/2014.
 */
public class BluetoothConnectivityService extends DataService
{
    public static final String CONFIG_ROLE = "role";
    public static final String CONFIG_SLAVE_MAC = "slaveMac";

    public static final String SERVICE_NAME = "BluetoothConnectivityService";
    public static final String UUID_STRING = "96c2cf70-359b-11e4-8c21-0800200c9a66";
    public static final int TIME_SYNC_TRIPS = 8;

    private BluetoothAdapter mAdapter;
    private BluetoothThread mBluetoothThread;
    private DeserialiseDataTransform mDataTransform = new DeserialiseDataTransform();
    private boolean mIsMaster;
    private boolean mThreadStopExpected = false;

    private long mLatency;
    private long mTimeOffset;
    private int mTimeSyncs = 0;
    private long[] mMasterTimestamps = new long[TIME_SYNC_TRIPS+1];
    private long[] mSlaveTimestamps = new long[TIME_SYNC_TRIPS];

    @Override
    protected boolean isValidConfig(Data config)
    {
        if (config == null)
            return false;
        if (!config.contains(CONFIG_ROLE))
            return false;
        return true;
    }

    @Override
    protected void doStart() throws FailedInitialisationException
    {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mAdapter == null || !mAdapter.isEnabled())
        {
            mAdapter = null;
            throw new FailedInitialisationException("Bluetooth unavailable!");
        }
        mIsMaster = mConfig.get(CONFIG_ROLE);
        if (mIsMaster)
        {
            String slaveMac = mConfig.get(CONFIG_SLAVE_MAC);
            if (slaveMac == null)
                throw new FailedInitialisationException("Missing '" + CONFIG_SLAVE_MAC + "' string!");
            BluetoothDevice slaveDevice = getDeviceFromMac(slaveMac);
            if (slaveDevice == null)
            {
                throw new FailedInitialisationException("Specified Bluetooth device is not paired!");
            }
            // Create client socket
            UUID uuid = UUID.fromString(UUID_STRING);
            try
            {
                BluetoothSocket socket = slaveDevice.createRfcommSocketToServiceRecord(uuid);
                mBluetoothThread = new BluetoothThread(socket, this);
                mBluetoothThread.start();
            } catch (IOException e)
            {
                e.printStackTrace();
                throw new FailedInitialisationException("Failed to connect bluetooth socket! " + e.getMessage());
            }
        } else
        {
            // Create server socket
            UUID uuid = UUID.fromString(UUID_STRING);
            try
            {
                BluetoothServerSocket socket = mAdapter.listenUsingRfcommWithServiceRecord(SERVICE_NAME, uuid);
                mBluetoothThread = new BluetoothThread(socket, this);
                mBluetoothThread.start();
            } catch (IOException e)
            {
                e.printStackTrace();
                throw new FailedInitialisationException("Failed to listen on bluetooth server socket! " + e.getMessage());
            }
        }
    }

    @Override
    protected void doStop()
    {
        if(mBluetoothThread != null)
        {
            // Going to kill BluetoothThread on purpose, write CLOSING event
            mBluetoothThread.write(Event.CLOSING.ordinal());
            mBluetoothThread.cancel();
            mBluetoothThread = null;
        }
    }

    @Override
    public void onData(IDataSource source, Data data)
    {
        super.onData(source, data);
        if(source == mBluetoothThread) // Data from thread goes up to the listener or is timesyncing
        {
            handleThreadData(data);
        }
        else // Other data goes down through bluetooth thread
        {
            write(data);
        }
    }

    @Override
    public void onEvent(IEventSource source, Event event, Object arg)
    {
        boolean propagateEvent = false;
        if(source == mBluetoothThread)
        {
            propagateEvent = handleThreadEvent(event, arg);
        }
        if(propagateEvent)
        {
            mEventListener.onEvent(this, event, arg);
        }
    }

    private void handleThreadData(Data data)
    {
        // Propagate data to the data listener
        if(mState == State.STARTED && mDataListener != null)
        {
            data = mDataTransform.transform(data);
            if(data != null && mDataListener != null)
                mDataListener.onData(this, data);
        }
        else if(mIsMaster && mState == State.STARTING) // Handle time syncing
        {
            byte[] byteData = data.get("bytes");
            mSlaveTimestamps[mTimeSyncs] = ByteUtils.bytesToLong(byteData, 0);
            ++mTimeSyncs;
            if(mTimeSyncs >= TIME_SYNC_TRIPS)
            {
                // Save last timestamp
                mMasterTimestamps[mTimeSyncs] = System.currentTimeMillis();
                // Calculate delta (round trip time for all
                // where t2 = t1 (assuming no time lost during slave processing
                long minDelta = Long.MAX_VALUE;
                int minDeltaIndex = 0;
                for(int i = 0; i < TIME_SYNC_TRIPS; ++i)
                {
                    long delta = mMasterTimestamps[i+1] - mMasterTimestamps[i];
                    if(delta < minDelta)
                    {
                        minDelta = delta;
                        mLatency = minDelta / 2;
                        minDeltaIndex = i;
                    }
                }
                // Use data points for smallest delta to calculate theta - time offset
                int i = minDeltaIndex;
                mTimeOffset = ((mSlaveTimestamps[i] - mMasterTimestamps[i]) + (mSlaveTimestamps[i] - mMasterTimestamps[i+1])) / 2;
                mBluetoothThread.write(Event.READY.ordinal());
                changeState(State.STARTED);

            }
            else
            {
                long time = System.currentTimeMillis();
                mMasterTimestamps[mTimeSyncs] = time;
                mBluetoothThread.write(Event.TIMESYNC.ordinal());
            }
        }

    }

    /**
     *
     * @param event
     * @param arg
     * @return Returns true if event should be propagated to the listener of the service
     */
    private boolean handleThreadEvent(Event event, Object arg)
    {
        switch (event)
        {
            case CONNECTED: // Thread connection establised, initiate timesyncing if master
                if(mIsMaster)
                {
                    logd("Timesyncing");
                    long time = System.currentTimeMillis();
                    mMasterTimestamps[0] = time;
                    mBluetoothThread.write(Event.TIMESYNC.ordinal());
                }
                break;
            case FAILED: // Thread has failed to connect, arg should contain reason
                failed(arg instanceof String ? (String)arg : "Unknown reason for bluetooth thread failure!");
                break;
            case STOPPING: // Thread has closed, flag determines normal or not!
                if(mThreadStopExpected)
                {
                    stop();
                }
                else
                {
                    // Unexpected, this is a fail
                    failed("Bluetooth connection closed unexpectedly");
                }
                break;
            case CLOSING: // Other end of connection is being closed normally, be cool
                mThreadStopExpected = true;
                break;
            case READY:
                changeState(State.STARTED);
                break;
            case TIMESYNC: // Other end of connection wants to timesync, provide!
                byte[] time = ByteUtils.longToBytes(System.currentTimeMillis());
                mBluetoothThread.write(time);
                break;
            default: // Event not related to service-thread interactions, propagate!
                return true;
        }
        return false; // Event must of hit a branch that is NOT default, therefore related to service-thread stuff, don't propagate
    }

    public void write(Data d)
    {
        if(mState == State.STARTED && mBluetoothThread != null)
        {
            byte[] bytes = SerialisationUtils.serialise(d);
            if(bytes == null || bytes.length > BluetoothThread.BYTE_BUFFER_SIZE)
            {
                logd("Null bytes, or bytes exceeding the buffer limit!!");
                return;
            }
            mBluetoothThread.write(bytes);
        }
    }

    public void write(Event e)
    {
        if(mState == State.STARTED && mBluetoothThread != null)
        {
            mBluetoothThread.write((byte)e.ordinal());
        }
    }

    public long getTimeOffset()
    {
        return mTimeOffset;
    }

    public long getLatency()
    {
        return mLatency;
    }

    private BluetoothDevice getDeviceFromMac(String mac)
    {
        Set<BluetoothDevice> devices = mAdapter.getBondedDevices();
        for(BluetoothDevice device : devices)
        {
            if(mac.equals(device.getAddress()))
            {
                return device;
            }
        }
        return null;
    }
}




Java Source Code List

com.example.DataCaptureApp.AdvConfigActivity.java
com.example.DataCaptureApp.ConfigActivity.java
com.example.DataCaptureApp.MasterActivity.java
com.example.DataCaptureApp.MasterService.java
com.example.DataCaptureApp.SlaveActivity.java
com.example.DataCaptureApp.SlaveService.java
com.example.DataCaptureApp.data.DataServiceConnection.java
com.example.DataCaptureApp.data.DataService.java
com.example.DataCaptureApp.data.DataTransform.java
com.example.DataCaptureApp.data.Data.java
com.example.DataCaptureApp.data.Event.java
com.example.DataCaptureApp.data.FailedInitialisationException.java
com.example.DataCaptureApp.data.IDataEventListener.java
com.example.DataCaptureApp.data.IDataListener.java
com.example.DataCaptureApp.data.IDataSource.java
com.example.DataCaptureApp.data.IDataTransform.java
com.example.DataCaptureApp.data.IEventListener.java
com.example.DataCaptureApp.data.IEventSource.java
com.example.DataCaptureApp.services.BluetoothConnectivityService.java
com.example.DataCaptureApp.services.BluetoothThread.java
com.example.DataCaptureApp.services.DataDbContract.java
com.example.DataCaptureApp.services.DataDbHelper.java
com.example.DataCaptureApp.services.DataStoreService.java
com.example.DataCaptureApp.services.HttpThread.java
com.example.DataCaptureApp.services.RemoteConnectivityService.java
com.example.DataCaptureApp.services.SensorSampleService.java
com.example.DataCaptureApp.services.SensorSampler.java
com.example.DataCaptureApp.testing.BluetoothActivity.java
com.example.DataCaptureApp.testing.DataStoreActivity.java
com.example.DataCaptureApp.testing.DataTester.java
com.example.DataCaptureApp.testing.IServiceListener.java
com.example.DataCaptureApp.testing.MainService.java
com.example.DataCaptureApp.testing.MasterTestActivity.java
com.example.DataCaptureApp.testing.RandomService.java
com.example.DataCaptureApp.testing.RemoteActivity.java
com.example.DataCaptureApp.testing.SensorSampleActivity.java
com.example.DataCaptureApp.testing.ServiceTestActivity.java
com.example.DataCaptureApp.testing.SlaveTestActivity.java
com.example.DataCaptureApp.testing.TestActivity.java
com.example.DataCaptureApp.transforms.AggregatorDataTransform.java
com.example.DataCaptureApp.transforms.ArithmeticDataTransform.java
com.example.DataCaptureApp.transforms.ArrayCollectDataTransform.java
com.example.DataCaptureApp.transforms.ArraySplitDataTransform.java
com.example.DataCaptureApp.transforms.DeserialiseDataTransform.java
com.example.DataCaptureApp.transforms.FieldCopyDataTransform.java
com.example.DataCaptureApp.transforms.FieldModifyDataTransform.java
com.example.DataCaptureApp.transforms.FieldRenameDataTransform.java
com.example.DataCaptureApp.transforms.IntervalAggregatorDataTransform.java
com.example.DataCaptureApp.transforms.PackDataTransform.java
com.example.DataCaptureApp.transforms.QuaternionDifferenceDataTransform.java
com.example.DataCaptureApp.transforms.RemoveDataTransform.java
com.example.DataCaptureApp.transforms.SetDataTransform.java
com.example.DataCaptureApp.transforms.UnpackDataTransform.java
com.example.DataCaptureApp.utils.BroadcastDataSource.java
com.example.DataCaptureApp.utils.ByteUtils.java
com.example.DataCaptureApp.utils.DataEventHandler.java
com.example.DataCaptureApp.utils.DataHandlerThread.java
com.example.DataCaptureApp.utils.JSONReader.java
com.example.DataCaptureApp.utils.Quaternion.java
com.example.DataCaptureApp.utils.SerialisationUtils.java