/*******************************************************************************
* Code contributed to the webinos project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2012 Ziran Sun, Samsung Electronics(UK) Ltd
*
******************************************************************************/
package org.webinos.android.impl.discovery;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.ArrayList;
import android.util.Log;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
import org.meshpoint.anode.AndroidContext;
import org.meshpoint.anode.idl.Dictionary;
import org.meshpoint.anode.module.IModule;
import org.meshpoint.anode.module.IModuleContext;
import org.webinos.api.PendingOperation;
import org.webinos.api.discovery.DiscoveryManager;
import org.webinos.api.discovery.Filter;
import org.webinos.api.discovery.FindCallback;
import org.webinos.api.discovery.Options;
import org.webinos.api.discovery.Service;
import org.webinos.api.discovery.ServiceType;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
public class DiscoveryHRMImpl extends DiscoveryManager implements IModule {
private Context androidContext;
private BluetoothAdapter mBluetoothAdapter;
private static final String TAG = "org.webinos.android.impl.DiscoveryHRMImpl";
private static final boolean D = true;
/* hard coded array length */
ArrayList<BluetoothDevice> devicesAvailable = new ArrayList<BluetoothDevice>(10);
ArrayList<BluetoothDevice> devicesFound = new ArrayList<BluetoothDevice>(10);
DiscoveryServiceImpl srv = new DiscoveryServiceImpl();
//DEMO parameters
private BluetoothSocket mmSocket;
private String mHxMName = null;
private String mHxMAddress = null;
private ConnectedThread mConnectedThread = null;
/*****************************
* DiscoveryManager methods
*****************************/
@Override
public synchronized PendingOperation findServices(
ServiceType serviceType,
FindCallback findCallback,
Options options,
Filter filter)
{
if(D) Log.v(TAG, "DiscoveryHRMImpl: findservices");
if(serviceType == null) {
Log.e(TAG, "DiscoveryHRMImpl: Please specify a serviceType");
return null;
}
//Check on Bluetooth availability
mBluetoothAdapter = getDefaultBluetoothAdapter();
if (mBluetoothAdapter == null) {
if(D) Log.e(TAG, "Bluetooth is not available");
return null;
}
else{
if (!mBluetoothAdapter.isEnabled()){
if(D) Log.d(TAG, "Bluetooth is not enabled");
return null;
}
else
{
Log.d(TAG, "Found Bluetooth adapter");
//Start FindService
DiscoveryRunnable bluetoothFindService = new BluetoothFindService(serviceType, findCallback, options, filter);
Thread t = new Thread(bluetoothFindService);
t.start();
Log.v(TAG, "findServices - thread started with id "+(int)t.getId());
return new DiscoveryPendingOperation(t, bluetoothFindService);
}
}
}
public void advertServices(String serviceType){
//start advertisement
}
public String getServiceId(String serviceType){
// TODO Auto-generated method stub - this probably is not applicable for BT discovery
return null;
}
public Service createService(){
DiscoveryServiceImpl srv = new DiscoveryServiceImpl();
return srv;
}
/*****************************
* IModule methods
*****************************/
@Override
public Object startModule(IModuleContext ctx) {
if(D) Log.v(TAG, "DiscoveryHRMImpl: startModule");
androidContext = ((AndroidContext)ctx).getAndroidContext();
return this;
}
@Override
public void stopModule() {
/*
* perform any module shutdown here ...
*/
if (mConnectedThread != null) {
mConnectedThread.cancel(); mConnectedThread = null;
}
Log.v(TAG, "DiscoveryHRMImpl: stopModule");
}
/*****************************
* Helpers
*****************************/
private static BluetoothAdapter getDefaultBluetoothAdapter() {
final ArrayList<BluetoothAdapter> adapters = new ArrayList<BluetoothAdapter>(1);
if(adapters.isEmpty()) {
final boolean[] ok = new boolean[] { false };
Thread t = new Thread() {
public void run() {
Looper.prepare();
adapters.add(BluetoothAdapter.getDefaultAdapter());
synchronized (ok) {
ok[0]=true;
ok.notify();
}
Looper.loop();
};
};
t.start();
synchronized (ok) {
if (ok[0]==false) {
try {
ok.wait();
} catch (InterruptedException e) {}
}
}
}
return adapters.get(0);
}
class BluetoothFindService implements DiscoveryRunnable {
private ServiceType serviceType;
private FindCallback findCallback;
private Options options;
private Filter filter;
private boolean stopped;
private BluetoothFindService(ServiceType srvType,
FindCallback findCB,
Options opts,
Filter fltr) {
serviceType = srvType;
findCallback = findCB;
options = opts;
filter = fltr;
stopped = false;
if(D) Log.v(TAG,"constructed BluetoothFindService");
}
public synchronized boolean isStopped() {
return stopped;
}
public synchronized void stop() {
stopped = true;
}
public void run() {
//Simply connect to the HRM device
Log.d(TAG, "Connecting to HRM");
//get the list of bonded devices
Set<BluetoothDevice> devicesPaired = mBluetoothAdapter.getBondedDevices();
if (devicesPaired.isEmpty() && devicesAvailable.isEmpty())
Log.e(TAG, "No bluetooth device is available");
//TODO: check if HRM is paired
else{
//Assume that HXM device is paired
if(!devicesPaired.isEmpty()){
for (BluetoothDevice device : devicesPaired) {
String deviceName = device.getName();
if ( deviceName.startsWith("HXM") ) {
/*
* we found an HxM to try to talk to!, let's remember its name and
* stop looking for more
*/
mHxMAddress = device.getAddress();
mHxMName = device.getName();
Log.d(TAG,"getConnectedHxm() found a device whose name starts with 'HXM', its name is "+mHxMName+" and its address is ++mHxMAddress");
// start connecting to Hxm
BluetoothSocket tmp = null;
try {
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
tmp = (BluetoothSocket) m.invoke(device, 1);
}catch (SecurityException e) {
Log.e(TAG, "ConnectThread() SecurityException");
e.printStackTrace();
} catch (NoSuchMethodException e) {
Log.e(TAG, "ConnectThread() SecurityException");
e.printStackTrace();
} catch (IllegalArgumentException e) {
Log.e(TAG, "ConnectThread() SecurityException");
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(TAG, "ConnectThread() SecurityException");
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "ConnectThread() SecurityException");
e.printStackTrace();
}
mmSocket = tmp;
try {
// This is a blocking call and will only return on a successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
//inform widget that socket is not connected
long[] values = {1000, 0 , 0, 0, 0, 0};
srv.values = values;
findCallback.onFound(srv);
Log.d(TAG, "END connectionFailed");
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "ConnectThread.run(): unable to close() socket during connection failure", e2);
}
}
// srv.api = serviceType.api;
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel(); mConnectedThread = null;
}
mConnectedThread = new ConnectedThread(mmSocket, findCallback);
mConnectedThread.start();
}
}
}
}
} //end of run
} // end of runnable
/*
* This thread runs during a connection with the Hxm.
* It handles all incoming data
*/
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final FindCallback findCallback;
private final InputStream mmInStream;
public ConnectedThread(BluetoothSocket socket, FindCallback findCB) {
Log.d(TAG, "ConnectedThread(): starting");
mmSocket = socket;
findCallback = findCB;
InputStream tmpIn = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
Log.e(TAG, "ConnectedThread(): temp sockets not created", e);
}
mmInStream = tmpIn;
Log.d(TAG, "ConnectedThread(): finished");
}
/*
* The code below is a basic implementation of a reader specific to the HxM device. It is
* intended to illustrate the packet structure and field extraction. Consider if your
* implementation should include more robust error detection logic to prevent things like
* buffer sizes from causing read overruns, or recomputing the CRC and comparing it to the
* contents of the message to detect transmission erros.
*/
private final int STX = 0x02;
private final int MSGID = 0x26;
private final int DLC = 55;
private final int ETX = 0x03;
@Override
public void run() {
Log.d(TAG, "ConnectedThread.run(): starting");
byte[] buffer = new byte[1024];
int b = 0;
int bufferIndex = 0;
int payloadBytesRemaining;
// Keep listening to the InputStream while connected
while (true) {
try {
bufferIndex = 0;
// Read bytes from the stream until we encounter the the start of message character
while (( b = mmInStream.read()) != STX )
;
buffer[bufferIndex++] = (byte) b;
// The next byte must be the message ID, see the basic message format in the document
if ((b = mmInStream.read()) != MSGID )
continue;
buffer[bufferIndex++] = (byte) b;
// The next byte must be the expected data length code, we don't handle variable length messages, see the doc
if ((b = mmInStream.read()) != DLC )
continue;
buffer[bufferIndex++] = (byte) b;
payloadBytesRemaining = b;
while ( (payloadBytesRemaining--) > 0 ) {
buffer[bufferIndex++] = (byte) (b = mmInStream.read());
}
// The next byte should be a CRC
buffer[bufferIndex++] = (byte) (b = mmInStream.read());
// The next byte must be the end of text indicator, or there was sadness, see the basic message format in the document
if ((b = mmInStream.read()) != ETX )
continue;
buffer[bufferIndex++] = (byte) b;
Log.d(TAG, "mConnectedThread: read "+Integer.toString(bufferIndex)+" bytes");
long[] values = {0, 0 , 0, 0, 0, 0};
srv.values = values;
HrmReading hrm = new HrmReading( buffer, srv.values);
findCallback.onFound(srv);
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
//connectionLost();
break;
}
}
Log.d(TAG, "ConnectedThread.run(): finished");
}
public void cancel() {
Log.e(TAG, "cancel connection");
try {
mmInStream.close();} catch (IOException e) {
Log.e(TAG, "ConnectedThread.cancel(): close() of InputStream failed", e);
}
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "ConnectedThread.cancel(): close() of connect socket failed", e);
}
}
}
//start of HrmReading
public class HrmReading implements Dictionary {
public final int STX = 0x02;
public final int MSGID = 0x26;
public final int DLC = 55;
public final int ETX = 0x03;
private static final String TAG = "HrmReading";
int serial;
byte stx;
byte msgId;
byte dlc;
int firmwareId;
int firmwareVersion;
int hardWareId;
int hardwareVersion;
int batteryIndicator;
int heartRate;
int heartBeatNumber;
long hbTime1;
long hbTime2;
long hbTime3;
long hbTime4;
long hbTime5;
long hbTime6;
long hbTime7;
long hbTime8;
long hbTime9;
long hbTime10;
long hbTime11;
long hbTime12;
long hbTime13;
long hbTime14;
long hbTime15;
long reserved1;
long reserved2;
long reserved3;
long distance;
long speed;
byte strides;
byte reserved4;
long reserved5;
byte crc;
byte etx;
long[] values = {0, 0 , 0, 0, 0, 0};
public HrmReading (byte[] buffer, long[] val) {
int bufferIndex = 0;
values = val;
Log.d ( TAG, "HrmReading being built from byte buffer");
try {
stx = buffer[bufferIndex++];
msgId = buffer[bufferIndex++];
dlc = buffer[bufferIndex++];
firmwareId = (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
firmwareVersion = (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hardWareId = (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hardwareVersion = (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
batteryIndicator = (int)(0x000000FF & (int)(buffer[bufferIndex++]));
heartRate = (int)(0x000000FF & (int)(buffer[bufferIndex++]));
heartBeatNumber = (int)(0x000000FF & (int)(buffer[bufferIndex++]));
hbTime1 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime2 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime3 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime4 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime5 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime6 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime7 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime8 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime9 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime10 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime11 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime12 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime13 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime14 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
hbTime15 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
reserved1 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
reserved2 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
reserved3 = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
distance = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
speed = (long) (int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
strides = buffer[bufferIndex++];
reserved4 = buffer[bufferIndex++];
reserved5 = (long)(int)((0x000000FF & (int)buffer[bufferIndex++]) | (int)(0x000000FF & (int)buffer[bufferIndex++])<< 8);
crc = buffer[bufferIndex++];
etx = buffer[bufferIndex];
} catch (Exception e) {
Log.d(TAG, "Failure building HrmReading from byte buffer, probably an incopmplete or corrupted buffer");
}
Log.d(TAG, "Building HrmReading from byte buffer complete, consumed " + bufferIndex + " bytes in the process");
if ( etx != ETX )
Log.e(TAG,"...ETX mismatch! The HxM message was not parsed properly");
//pass values to srv
Log.d(TAG,"...heartRate "+ ( heartRate ));
val[0] = (long)(int)heartRate;
Log.d(TAG,"...heartBeatNumber "+ ( heartBeatNumber ));
val[1] = (long)(int)heartBeatNumber;
Log.d(TAG,"...distance "+ ( distance ));
val[2] = distance;
Log.d(TAG,"...speed "+ ( speed ));
val[3] = speed;
Log.d(TAG,"...strides "+ ( strides ));
val[4] = strides;
}
}
// end of HXM reading
abstract interface DiscoveryRunnable extends Runnable {
//for supports on PendingOperation
public abstract void stop();
public abstract boolean isStopped();
}
class DiscoveryPendingOperation extends PendingOperation {
private Thread t=null;
private DiscoveryRunnable r=null;
public DiscoveryPendingOperation(Thread t, DiscoveryRunnable r) {
this.t = t;
this.r = r;
}
public void cancel() {
Log.d(TAG, "DiscoveryPendingOperation cancel");
if(t!=null) {
Log.v(TAG, "DiscoveryPendingOperation cancel - send interrupt...");
//Is this interrupt needed??? - copied from messaging
t.interrupt();
if(r!=null)
r.stop();
}
}
}
}