Pebble 2: add not yet working code (at least not without weird workarounds)
This is is a pain because of tons of weird pairing issues
This commit is contained in:
parent
d89899557c
commit
00a71f53b3
|
@ -42,6 +42,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PBWReader;
|
|||
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleInstallable;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble.PebbleLESupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
|
@ -75,6 +76,8 @@ class PebbleIoThread extends GBDeviceIoThread {
|
|||
private Socket mTCPSocket = null; // for emulator
|
||||
private InputStream mInStream = null;
|
||||
private OutputStream mOutStream = null;
|
||||
private PebbleLESupport mPebbleLESupport;
|
||||
|
||||
private boolean mQuit = false;
|
||||
private boolean mIsConnected = false;
|
||||
private boolean mIsInstalling = false;
|
||||
|
@ -180,12 +183,11 @@ class PebbleIoThread extends GBDeviceIoThread {
|
|||
mIsTCP = false;
|
||||
BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(btDeviceAddress);
|
||||
if (btDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE) {
|
||||
LOG.info("Ok this seems to be a LE Pebble, will try something that does not work :P");
|
||||
mInStream = new PipedInputStream(); // fake so that io blocks
|
||||
mOutStream = new PipedOutputStream(); // fake so that io blocks
|
||||
//new PebbleLESupport(this.getContext(),btDeviceAddress,(PipedInputStream)mInStream,(PipedOutputStream)mOutStream); // secret branch :P
|
||||
}
|
||||
else {
|
||||
LOG.info("Ok this seems to be a LE Pebble, try LE Support, trouble ahead!");
|
||||
mInStream = new PipedInputStream();
|
||||
mOutStream = new PipedOutputStream();
|
||||
mPebbleLESupport = new PebbleLESupport(this.getContext(),btDeviceAddress,(PipedInputStream)mInStream,(PipedOutputStream)mOutStream); // secret branch :P
|
||||
} else {
|
||||
ParcelUuid uuids[] = btDevice.getUuids();
|
||||
if (uuids == null) {
|
||||
return false;
|
||||
|
@ -708,6 +710,7 @@ class PebbleIoThread extends GBDeviceIoThread {
|
|||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mBtSocket = null;
|
||||
}
|
||||
if (mTCPSocket != null) {
|
||||
try {
|
||||
|
@ -715,6 +718,11 @@ class PebbleIoThread extends GBDeviceIoThread {
|
|||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mTCPSocket = null;
|
||||
}
|
||||
if (mPebbleLESupport != null) {
|
||||
mPebbleLESupport.close();
|
||||
mPebbleLESupport = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.content.Context;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE;
|
||||
|
||||
|
||||
class PebbleGATTClient extends BluetoothGattCallback {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PebbleGATTClient.class);
|
||||
|
||||
private static final UUID SERVICE_UUID = UUID.fromString("0000fed9-0000-1000-8000-00805f9b34fb");
|
||||
private static final UUID CONNECTIVITY_CHARACTERISTIC = UUID.fromString("00000001-328E-0FBB-C642-1AA6699BDADA");
|
||||
private static final UUID PAIRING_TRIGGER_CHARACTERISTIC = UUID.fromString("00000002-328E-0FBB-C642-1AA6699BDADA");
|
||||
private static final UUID MTU_CHARACTERISTIC = UUID.fromString("00000003-328e-0fbb-c642-1aa6699bdada");
|
||||
private static final UUID CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.fromString("00000005-328E-0FBB-C642-1AA6699BDADA");
|
||||
private static final UUID CHARACTERISTIC_CONFIGURATION_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
private final String mBtDeviceAddress;
|
||||
private final BluetoothDevice mBtDevice;
|
||||
private final Context mContext;
|
||||
|
||||
private boolean oldPebble = false;
|
||||
private boolean doPairing = true;
|
||||
private boolean removeBond = false;
|
||||
private BluetoothGatt mBluetoothGatt;
|
||||
|
||||
PebbleGATTClient(Context context, BluetoothDevice btDevice) {
|
||||
mContext = context;
|
||||
mBtDevice = btDevice;
|
||||
mBtDeviceAddress = btDevice.getAddress();
|
||||
}
|
||||
|
||||
boolean initialize() {
|
||||
connectToPebble(mBtDevice);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) {
|
||||
LOG.info("onCharacteristicChanged() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress);
|
||||
return;
|
||||
}
|
||||
LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1));
|
||||
}
|
||||
|
||||
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) {
|
||||
LOG.info("onCharacteristicRead() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress);
|
||||
return;
|
||||
}
|
||||
LOG.info("onCharacteristicRead() status = " + status);
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
LOG.info("onCharacteristicRead()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1));
|
||||
|
||||
if (oldPebble) {
|
||||
subscribeToConnectivity(gatt);
|
||||
} else {
|
||||
subscribeToConnectionParams(gatt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||
if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) {
|
||||
LOG.info("onConnectionStateChange() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress);
|
||||
return;
|
||||
}
|
||||
LOG.info("onConnectionStateChange() status = " + status + " newState = " + newState);
|
||||
if (newState == BluetoothGatt.STATE_CONNECTED) {
|
||||
LOG.info("calling discoverServices()");
|
||||
gatt.discoverServices();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) {
|
||||
LOG.info("onCharacteristcsWrite unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress);
|
||||
return;
|
||||
}
|
||||
LOG.info("onCharacteristicWrite() " + characteristic.getUuid());
|
||||
if (characteristic.getUuid().equals(PAIRING_TRIGGER_CHARACTERISTIC) || characteristic.getUuid().equals(CONNECTIVITY_CHARACTERISTIC)) {
|
||||
mBtDevice.createBond(); // did not work when last tried
|
||||
|
||||
if (oldPebble) {
|
||||
subscribeToConnectivity(gatt);
|
||||
} else {
|
||||
subscribeToConnectionParams(gatt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor bluetoothGattDescriptor, int status) {
|
||||
if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) {
|
||||
LOG.info("onDescriptorWrite() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress);
|
||||
return;
|
||||
}
|
||||
LOG.info("onDescriptorWrite() status=" + status);
|
||||
|
||||
UUID CHARACTERISTICUUID = bluetoothGattDescriptor.getCharacteristic().getUuid();
|
||||
|
||||
if (CHARACTERISTICUUID.equals(CONNECTION_PARAMETERS_CHARACTERISTIC)) {
|
||||
subscribeToConnectivity(gatt);
|
||||
} else if (CHARACTERISTICUUID.equals(CONNECTIVITY_CHARACTERISTIC)) {
|
||||
subscribeToMTU(gatt);
|
||||
} else if (CHARACTERISTICUUID.equals(MTU_CHARACTERISTIC)) {
|
||||
setMTU(gatt);
|
||||
}
|
||||
}
|
||||
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
if (!gatt.getDevice().getAddress().equals(mBtDeviceAddress)) {
|
||||
LOG.info("onServicesDiscovered() unexpected device: " + gatt.getDevice().getAddress() + " , expected: " + mBtDeviceAddress);
|
||||
return;
|
||||
}
|
||||
LOG.info("onServicesDiscovered() status = " + status);
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
BluetoothGattCharacteristic connectionPararmharacteristic = gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTION_PARAMETERS_CHARACTERISTIC);
|
||||
oldPebble = connectionPararmharacteristic == null;
|
||||
|
||||
if (oldPebble) {
|
||||
LOG.info("This seems to be an older le enabled pebble");
|
||||
}
|
||||
|
||||
if (doPairing) {
|
||||
BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(PAIRING_TRIGGER_CHARACTERISTIC);
|
||||
if ((characteristic.getProperties() & PROPERTY_WRITE) != 0) {
|
||||
characteristic.setValue(new byte[]{0, 1}); // bits 0=1 1=no slave sec 2=kk 3=samsung kk
|
||||
gatt.writeCharacteristic(characteristic);
|
||||
}
|
||||
else {
|
||||
LOG.info("This seems to be some <4.0 FW Pebble, reading pairing trigger");
|
||||
gatt.readCharacteristic(characteristic);
|
||||
}
|
||||
} else {
|
||||
if (oldPebble) {
|
||||
subscribeToConnectivity(gatt);
|
||||
} else {
|
||||
subscribeToConnectionParams(gatt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void connectToPebble(BluetoothDevice btDevice) {
|
||||
if (removeBond) {
|
||||
try {
|
||||
Method m = btDevice.getClass()
|
||||
.getMethod("removeBond", (Class[]) null);
|
||||
m.invoke(btDevice, (Object[]) null);
|
||||
} catch (Exception e) {
|
||||
LOG.warn(e.getMessage());
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
if (mBluetoothGatt != null) {
|
||||
this.close();
|
||||
}
|
||||
mBtDevice.createBond();
|
||||
mBluetoothGatt = btDevice.connectGatt(mContext, false, this);
|
||||
}
|
||||
|
||||
private void subscribeToConnectivity(BluetoothGatt gatt) {
|
||||
LOG.info("subscribing to connectivity characteristic");
|
||||
BluetoothGattDescriptor descriptor = gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTIVITY_CHARACTERISTIC).getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR);
|
||||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
|
||||
gatt.writeDescriptor(descriptor);
|
||||
gatt.setCharacteristicNotification(gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTIVITY_CHARACTERISTIC), true);
|
||||
}
|
||||
|
||||
private void subscribeToMTU(BluetoothGatt gatt) {
|
||||
LOG.info("subscribing to mtu characteristic");
|
||||
BluetoothGattDescriptor descriptor = gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC).getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR);
|
||||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
|
||||
gatt.writeDescriptor(descriptor);
|
||||
gatt.setCharacteristicNotification(gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC), true);
|
||||
}
|
||||
|
||||
private void subscribeToConnectionParams(BluetoothGatt gatt) {
|
||||
LOG.info("subscribing to connection parameters characteristic");
|
||||
BluetoothGattDescriptor descriptor = gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTION_PARAMETERS_CHARACTERISTIC).getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR);
|
||||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
|
||||
gatt.writeDescriptor(descriptor);
|
||||
gatt.setCharacteristicNotification(gatt.getService(SERVICE_UUID).getCharacteristic(CONNECTION_PARAMETERS_CHARACTERISTIC), true);
|
||||
}
|
||||
|
||||
private void setMTU(BluetoothGatt gatt) {
|
||||
LOG.info("setting MTU");
|
||||
BluetoothGattCharacteristic characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(MTU_CHARACTERISTIC);
|
||||
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR);
|
||||
descriptor.setValue(new byte[]{0x0b, 0x01}); // unknown
|
||||
gatt.writeCharacteristic(characteristic);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (mBluetoothGatt != null) {
|
||||
mBluetoothGatt.disconnect();
|
||||
mBluetoothGatt.close();
|
||||
mBluetoothGatt = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattServer;
|
||||
import android.bluetooth.BluetoothGattServerCallback;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.Context;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
class PebbleGATTServer extends BluetoothGattServerCallback {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PebbleGATTServer.class);
|
||||
private static final UUID WRITE_CHARACTERISTICS = UUID.fromString("10000001-328E-0FBB-C642-1AA6699BDADA");
|
||||
private static final UUID READ_CHARACTERISTICS = UUID.fromString("10000002-328E-0FBB-C642-1AA6699BDADA");
|
||||
private static final UUID CHARACTERISTICS_CONFIGURATION_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||
private static final UUID SERVER_SERVICE = UUID.fromString("10000000-328E-0FBB-C642-1AA6699BDADA");
|
||||
private static final UUID SERVER_SERVICE_BADBAD = UUID.fromString("BADBADBA-DBAD-BADB-ADBA-BADBADBADBAD");
|
||||
private final BluetoothDevice mBtDevice;
|
||||
private final PebbleLESupport mPebbleLESupport;
|
||||
private Context mContext;
|
||||
private BluetoothGattServer mBluetoothGattServer;
|
||||
private BluetoothGattCharacteristic writeCharacteristics;
|
||||
|
||||
PebbleGATTServer(PebbleLESupport pebbleLESupport, Context context, BluetoothDevice btDevice) {
|
||||
mContext = context;
|
||||
mBtDevice = btDevice;
|
||||
mPebbleLESupport = pebbleLESupport;
|
||||
}
|
||||
|
||||
boolean initialize() {
|
||||
BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
|
||||
mBluetoothGattServer = bluetoothManager.openGattServer(mContext, this);
|
||||
|
||||
BluetoothGattService pebbleGATTService = new BluetoothGattService(SERVER_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY);
|
||||
pebbleGATTService.addCharacteristic(new BluetoothGattCharacteristic(READ_CHARACTERISTICS, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ));
|
||||
|
||||
writeCharacteristics = new BluetoothGattCharacteristic(WRITE_CHARACTERISTICS, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_WRITE);
|
||||
|
||||
writeCharacteristics.addDescriptor(new BluetoothGattDescriptor(CHARACTERISTICS_CONFIGURATION_DESCRIPTOR, BluetoothGattDescriptor.PERMISSION_WRITE));
|
||||
pebbleGATTService.addCharacteristic(writeCharacteristics);
|
||||
mBluetoothGattServer.addService(pebbleGATTService);
|
||||
|
||||
|
||||
final BluetoothGattService badbadService = new BluetoothGattService(SERVER_SERVICE_BADBAD, BluetoothGattService.SERVICE_TYPE_PRIMARY);
|
||||
badbadService.addCharacteristic(new BluetoothGattCharacteristic(SERVER_SERVICE_BADBAD, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ));
|
||||
mBluetoothGattServer.addService(badbadService);
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized void sendDataToPebble(byte[] data) {
|
||||
LOG.info("send data to pebble " + GB.hexdump(data, 0, -1));
|
||||
writeCharacteristics.setValue(data.clone());
|
||||
|
||||
|
||||
mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false);
|
||||
|
||||
try {
|
||||
Thread.sleep(100); // FIXME: bad bad, I mean BAAAD
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized private void sendAckToPebble(int serial) {
|
||||
LOG.info("send ack to pebble for serial " + serial);
|
||||
|
||||
writeCharacteristics.setValue(new byte[]{(byte) (((serial << 3) | 1) & 0xff)});
|
||||
|
||||
mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false);
|
||||
|
||||
try {
|
||||
Thread.sleep(100); // FIXME: bad bad, I mean BAAAD
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
|
||||
if (!characteristic.getUuid().equals(READ_CHARACTERISTICS)) {
|
||||
LOG.warn("unexpected read request");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("will send response to read request from device: " + device.getAddress());
|
||||
if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, new byte[]{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})) {
|
||||
LOG.warn("error sending response");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic,
|
||||
boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
|
||||
if (!characteristic.getUuid().equals(WRITE_CHARACTERISTICS)) {
|
||||
LOG.warn("unexpected write request");
|
||||
return;
|
||||
}
|
||||
LOG.info("write request: offset = " + offset + " value = " + GB.hexdump(value, 0, -1));
|
||||
int header = value[0] & 0xff;
|
||||
int command = header & 7;
|
||||
int serial = header >> 3;
|
||||
if (command == 0x01) {
|
||||
LOG.info("got ACK for serial = " + serial);
|
||||
}
|
||||
if (command == 0x02) { // some request?
|
||||
LOG.info("got command 0x02");
|
||||
if (value.length > 1) {
|
||||
sendDataToPebble(new byte[]{0x03, 0x19, 0x19}); // no we dont know what that means
|
||||
mPebbleLESupport.createPipedInputReader(); // FIXME: maybe not here
|
||||
} else {
|
||||
sendDataToPebble(new byte[]{0x03}); // no we dont know what that means
|
||||
}
|
||||
} else if (command == 0) { // normal package
|
||||
LOG.info("got PPoGATT package serial = " + serial + " sending ACK");
|
||||
|
||||
sendAckToPebble(serial);
|
||||
|
||||
mPebbleLESupport.writeToPipedOutputStream(value, 1, value.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
|
||||
LOG.info("Connection state change for device: " + device.getAddress() + " status = " + status + " newState = " + newState);
|
||||
}
|
||||
|
||||
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor,
|
||||
boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
|
||||
|
||||
if (!descriptor.getCharacteristic().getUuid().equals(WRITE_CHARACTERISTICS)) {
|
||||
LOG.warn("unexpected write request");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("onDescriptorWriteRequest() notifications enabled = " + (value[0] == 1));
|
||||
if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, value)) {
|
||||
LOG.warn("onDescriptorWriteRequest() error sending response!");
|
||||
}
|
||||
}
|
||||
|
||||
public void onServiceAdded(int status, BluetoothGattService service) {
|
||||
LOG.info("onServiceAdded() status = " + status + " service = " + service.getUuid());
|
||||
}
|
||||
|
||||
public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) {
|
||||
//LOG.info("onNotificationSent() status = " + status + " to device " + mmBtDevice.getAddress());
|
||||
}
|
||||
|
||||
void close() {
|
||||
mBluetoothGattServer.cancelConnection(mBtDevice);
|
||||
mBluetoothGattServer.clearServices();
|
||||
mBluetoothGattServer.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGattServerCallback;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.Context;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
|
||||
public class PebbleLESupport extends BluetoothGattServerCallback {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PebbleLESupport.class);
|
||||
private PipeReader mPipeReader;
|
||||
private PebbleGATTServer mPebbleGATTServer;
|
||||
private PebbleGATTClient mPebbleGATTClient;
|
||||
private PipedInputStream mPipedInputStream;
|
||||
private PipedOutputStream mPipedOutputStream;
|
||||
|
||||
public PebbleLESupport(Context context, final String btDeviceAddress, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) {
|
||||
|
||||
mPipedInputStream = new PipedInputStream();
|
||||
mPipedOutputStream = new PipedOutputStream();
|
||||
try {
|
||||
pipedOutputStream.connect(mPipedInputStream);
|
||||
pipedInputStream.connect(mPipedOutputStream);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("could not connect input stream");
|
||||
}
|
||||
|
||||
BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
BluetoothAdapter adapter = manager.getAdapter();
|
||||
BluetoothDevice btDevice = adapter.getRemoteDevice(btDeviceAddress);
|
||||
mPebbleGATTServer = new PebbleGATTServer(this, context, btDevice);
|
||||
mPebbleGATTServer.initialize();
|
||||
|
||||
mPebbleGATTClient = new PebbleGATTClient(context, btDevice);
|
||||
mPebbleGATTClient.initialize();
|
||||
}
|
||||
|
||||
void writeToPipedOutputStream(byte[] value, int offset, int count) {
|
||||
try {
|
||||
mPipedOutputStream.write(value, offset, count);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("error writing to output stream");
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
destroyPipedInputReader();
|
||||
if (mPebbleGATTServer != null) {
|
||||
mPebbleGATTServer.close();
|
||||
mPebbleGATTServer = null;
|
||||
}
|
||||
if (mPebbleGATTClient != null) {
|
||||
mPebbleGATTClient.close();
|
||||
mPebbleGATTClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
void createPipedInputReader() {
|
||||
if (mPipeReader == null) {
|
||||
mPipeReader = new PipeReader();
|
||||
}
|
||||
if (!mPipeReader.isAlive()) {
|
||||
mPipeReader.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void destroyPipedInputReader() {
|
||||
if (mPipeReader != null) {
|
||||
mPipeReader.quit();
|
||||
mPipeReader.interrupt();
|
||||
try {
|
||||
mPipeReader.join();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
mPipeReader = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class PipeReader extends Thread {
|
||||
int mmSequence = 0;
|
||||
private boolean mQuit = false;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int MTU = 339 - 3;
|
||||
byte[] buf = new byte[8192];
|
||||
int bytesRead;
|
||||
while (!mQuit) {
|
||||
try {
|
||||
// this code is very similar to iothread, that is bad
|
||||
// because we are the ones who prepared the buffer, there should be no
|
||||
// need to do crazy stuff just to find out the PP boundaries again.
|
||||
bytesRead = mPipedInputStream.read(buf, 0, 4);
|
||||
while (bytesRead < 4) {
|
||||
bytesRead += mPipedInputStream.read(buf, bytesRead, 4 - bytesRead);
|
||||
}
|
||||
|
||||
int length = (buf[0] & 0xff) << 8 | (buf[1] & 0xff);
|
||||
bytesRead = mPipedInputStream.read(buf, 4, length);
|
||||
|
||||
while (bytesRead < length) {
|
||||
bytesRead += mPipedInputStream.read(buf, bytesRead + 4, length - bytesRead);
|
||||
}
|
||||
|
||||
|
||||
int payloadToSend = bytesRead + 4;
|
||||
int srcPos = 0;
|
||||
while (payloadToSend > 0) {
|
||||
int chunkSize = (payloadToSend < (MTU - 1)) ? payloadToSend : MTU - 1;
|
||||
byte[] outBuf = new byte[chunkSize + 1];
|
||||
outBuf[0] = (byte) ((mmSequence++ << 3) & 0xff);
|
||||
System.arraycopy(buf, srcPos, outBuf, 1, chunkSize);
|
||||
mPebbleGATTServer.sendDataToPebble(outBuf);
|
||||
srcPos += chunkSize;
|
||||
payloadToSend -= chunkSize;
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
LOG.warn("IO exception");
|
||||
mQuit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void quit() {
|
||||
mQuit = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue