mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-02-17 21:06:48 +01:00
Pebble2: WIP broken and disabled GATT client only support
This will eliminate the need for the Phone being a GATT Server and might lead to better connection stability once it is ready. NOTE: Enabling this in the code (change clientOnly=true in PebbleLESupport) will only work for a few packets before in totally breaks, so only enable if you want to fix it :P
This commit is contained in:
parent
bd012cab2a
commit
f6131772d6
@ -47,7 +47,13 @@ class PebbleGATTClient extends BluetoothGattCallback {
|
|||||||
private static final UUID CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.fromString("00000005-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 static final UUID CHARACTERISTIC_CONFIGURATION_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||||
|
|
||||||
private final BluetoothDevice mBtDevice;
|
//PPoGATT service (Pebble side)
|
||||||
|
private static final UUID PPOGATT_SERVICE_UUID = UUID.fromString("30000003-328E-0FBB-C642-1AA6699BDADA");
|
||||||
|
private static final UUID PPOGATT_CHARACTERISTIC_READ = UUID.fromString("30000004-328E-0FBB-C642-1AA6699BDADA");
|
||||||
|
private static final UUID PPOGATT_CHARACTERISTIC_WRITE = UUID.fromString("30000006-328E-0FBB-C642-1AA6699BDADA");
|
||||||
|
|
||||||
|
private BluetoothGattCharacteristic writeCharacteristics;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final PebbleLESupport mPebbleLESupport;
|
private final PebbleLESupport mPebbleLESupport;
|
||||||
|
|
||||||
@ -58,9 +64,8 @@ class PebbleGATTClient extends BluetoothGattCallback {
|
|||||||
|
|
||||||
PebbleGATTClient(PebbleLESupport pebbleLESupport, Context context, BluetoothDevice btDevice) {
|
PebbleGATTClient(PebbleLESupport pebbleLESupport, Context context, BluetoothDevice btDevice) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mBtDevice = btDevice;
|
|
||||||
mPebbleLESupport = pebbleLESupport;
|
mPebbleLESupport = pebbleLESupport;
|
||||||
connectToPebble(mBtDevice);
|
connectToPebble(btDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||||
@ -72,6 +77,8 @@ class PebbleGATTClient extends BluetoothGattCallback {
|
|||||||
int newMTU = characteristic.getIntValue(FORMAT_UINT16, 0);
|
int newMTU = characteristic.getIntValue(FORMAT_UINT16, 0);
|
||||||
LOG.info("Pebble requested MTU: " + newMTU);
|
LOG.info("Pebble requested MTU: " + newMTU);
|
||||||
mPebbleLESupport.setMTU(newMTU);
|
mPebbleLESupport.setMTU(newMTU);
|
||||||
|
} else if (characteristic.getUuid().equals(PPOGATT_CHARACTERISTIC_READ)) {
|
||||||
|
mPebbleLESupport.handlePPoGATTPacket(characteristic.getValue().clone());
|
||||||
} else {
|
} else {
|
||||||
LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1));
|
LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1));
|
||||||
}
|
}
|
||||||
@ -143,6 +150,12 @@ class PebbleGATTClient extends BluetoothGattCallback {
|
|||||||
} else if (CHARACTERISTICUUID.equals(CONNECTIVITY_CHARACTERISTIC)) {
|
} else if (CHARACTERISTICUUID.equals(CONNECTIVITY_CHARACTERISTIC)) {
|
||||||
subscribeToMTU(gatt);
|
subscribeToMTU(gatt);
|
||||||
} else if (CHARACTERISTICUUID.equals(MTU_CHARACTERISTIC)) {
|
} else if (CHARACTERISTICUUID.equals(MTU_CHARACTERISTIC)) {
|
||||||
|
if (mPebbleLESupport.clientOnly) {
|
||||||
|
subscribeToPPoGATT(gatt);
|
||||||
|
} else {
|
||||||
|
setMTU(gatt);
|
||||||
|
}
|
||||||
|
} else if (CHARACTERISTICUUID.equals(PPOGATT_CHARACTERISTIC_READ)) {
|
||||||
setMTU(gatt);
|
setMTU(gatt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,7 +184,11 @@ class PebbleGATTClient extends BluetoothGattCallback {
|
|||||||
// 2 - always 0
|
// 2 - always 0
|
||||||
// 3 - unknown, set on kitkat (seems to help to get a "better" pairing)
|
// 3 - unknown, set on kitkat (seems to help to get a "better" pairing)
|
||||||
// 4 - unknown, set on some phones
|
// 4 - unknown, set on some phones
|
||||||
characteristic.setValue(new byte[]{9});
|
if (mPebbleLESupport.clientOnly) {
|
||||||
|
characteristic.setValue(new byte[]{0x11}); // needed in clientOnly mode (TODO: try 0x19)
|
||||||
|
} else {
|
||||||
|
characteristic.setValue(new byte[]{0x09}); // I just keep this, because it worked
|
||||||
|
}
|
||||||
gatt.writeCharacteristic(characteristic);
|
gatt.writeCharacteristic(characteristic);
|
||||||
} else {
|
} else {
|
||||||
LOG.info("This seems to be some <4.0 FW Pebble, reading pairing trigger");
|
LOG.info("This seems to be some <4.0 FW Pebble, reading pairing trigger");
|
||||||
@ -239,6 +256,25 @@ class PebbleGATTClient extends BluetoothGattCallback {
|
|||||||
gatt.writeCharacteristic(characteristic);
|
gatt.writeCharacteristic(characteristic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void subscribeToPPoGATT(BluetoothGatt gatt) {
|
||||||
|
LOG.info("subscribing to PPoGATT read characteristic");
|
||||||
|
BluetoothGattDescriptor descriptor = gatt.getService(PPOGATT_SERVICE_UUID).getCharacteristic(PPOGATT_CHARACTERISTIC_READ).getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR);
|
||||||
|
descriptor.setValue(new byte[]{1, 0});
|
||||||
|
gatt.writeDescriptor(descriptor);
|
||||||
|
gatt.setCharacteristicNotification(gatt.getService(PPOGATT_SERVICE_UUID).getCharacteristic(PPOGATT_CHARACTERISTIC_READ), true);
|
||||||
|
writeCharacteristics = gatt.getService(PPOGATT_SERVICE_UUID).getCharacteristic(PPOGATT_CHARACTERISTIC_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void sendDataToPebble(byte[] data) {
|
||||||
|
writeCharacteristics.setValue(data.clone());
|
||||||
|
mBluetoothGatt.writeCharacteristic(writeCharacteristics);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void sendAckToPebble(int serial) {
|
||||||
|
writeCharacteristics.setValue(new byte[]{(byte) (((serial << 3) | 1) & 0xff)});
|
||||||
|
mBluetoothGatt.writeCharacteristic(writeCharacteristics);
|
||||||
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
if (mBluetoothGatt != null) {
|
if (mBluetoothGatt != null) {
|
||||||
mBluetoothGatt.disconnect();
|
mBluetoothGatt.disconnect();
|
||||||
|
@ -71,13 +71,12 @@ class PebbleGATTServer extends BluetoothGattServerCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized void sendDataToPebble(byte[] data) {
|
synchronized void sendDataToPebble(byte[] data) {
|
||||||
//LOG.info("send data to pebble " + GB.hexdump(data, 0, -1));
|
|
||||||
writeCharacteristics.setValue(data.clone());
|
writeCharacteristics.setValue(data.clone());
|
||||||
|
|
||||||
mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false);
|
mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized private void sendAckToPebble(int serial) {
|
synchronized void sendAckToPebble(int serial) {
|
||||||
writeCharacteristics.setValue(new byte[]{(byte) (((serial << 3) | 1) & 0xff)});
|
writeCharacteristics.setValue(new byte[]{(byte) (((serial << 3) | 1) & 0xff)});
|
||||||
|
|
||||||
mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false);
|
mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false);
|
||||||
@ -110,39 +109,7 @@ class PebbleGATTServer extends BluetoothGattServerCallback {
|
|||||||
LOG.warn("unexpected write request");
|
LOG.warn("unexpected write request");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!mPebbleLESupport.mIsConnected) {
|
mPebbleLESupport.handlePPoGATTPacket(value);
|
||||||
mPebbleLESupport.mIsConnected = true;
|
|
||||||
synchronized (mPebbleLESupport) {
|
|
||||||
mPebbleLESupport.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//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 (mPebbleLESupport.mPPAck != null) {
|
|
||||||
mPebbleLESupport.mPPAck.countDown();
|
|
||||||
} else {
|
|
||||||
LOG.warn("mPPAck countdownlatch is not present but it probably should");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (command == 0x02) { // some request?
|
|
||||||
LOG.info("got command 0x02");
|
|
||||||
if (value.length > 1) {
|
|
||||||
sendDataToPebble(new byte[]{0x03, 0x19, 0x19}); // no we don't know what that means
|
|
||||||
mPebbleLESupport.createPipedInputReader(); // FIXME: maybe not here
|
|
||||||
} else {
|
|
||||||
sendDataToPebble(new byte[]{0x03}); // no we don't 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) {
|
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
|
||||||
|
@ -39,8 +39,9 @@ public class PebbleLESupport {
|
|||||||
private PipedOutputStream mPipedOutputStream;
|
private PipedOutputStream mPipedOutputStream;
|
||||||
private int mMTU = 20;
|
private int mMTU = 20;
|
||||||
private int mMTULimit = Integer.MAX_VALUE;
|
private int mMTULimit = Integer.MAX_VALUE;
|
||||||
boolean mIsConnected = false;
|
public boolean clientOnly = false; // currently broken, and only possible for Pebble 2
|
||||||
CountDownLatch mPPAck;
|
private boolean mIsConnected = false;
|
||||||
|
private CountDownLatch mPPAck;
|
||||||
|
|
||||||
public PebbleLESupport(Context context, final BluetoothDevice btDevice, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) throws IOException {
|
public PebbleLESupport(Context context, final BluetoothDevice btDevice, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) throws IOException {
|
||||||
mBtDevice = btDevice;
|
mBtDevice = btDevice;
|
||||||
@ -56,8 +57,10 @@ public class PebbleLESupport {
|
|||||||
mMTULimit = Math.max(mMTULimit, 20);
|
mMTULimit = Math.max(mMTULimit, 20);
|
||||||
mMTULimit = Math.min(mMTULimit, 512);
|
mMTULimit = Math.min(mMTULimit, 512);
|
||||||
|
|
||||||
mPebbleGATTServer = new PebbleGATTServer(this, context, mBtDevice);
|
if (!clientOnly) {
|
||||||
if (mPebbleGATTServer.initialize()) {
|
mPebbleGATTServer = new PebbleGATTServer(this, context, mBtDevice);
|
||||||
|
}
|
||||||
|
if (clientOnly || mPebbleGATTServer.initialize()) {
|
||||||
mPebbleGATTClient = new PebbleGATTClient(this, context, mBtDevice);
|
mPebbleGATTClient = new PebbleGATTClient(this, context, mBtDevice);
|
||||||
try {
|
try {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@ -73,11 +76,11 @@ public class PebbleLESupport {
|
|||||||
throw new IOException("connection failed");
|
throw new IOException("connection failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeToPipedOutputStream(byte[] value, int offset, int count) {
|
private void writeToPipedOutputStream(byte[] value, int offset, int count) {
|
||||||
try {
|
try {
|
||||||
mPipedOutputStream.write(value, offset, count);
|
mPipedOutputStream.write(value, offset, count);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.warn("error writing to output stream");
|
LOG.warn("error writing to output stream", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +104,7 @@ public class PebbleLESupport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void createPipedInputReader() {
|
private synchronized void createPipedInputReader() {
|
||||||
if (mPipeReader == null) {
|
if (mPipeReader == null) {
|
||||||
mPipeReader = new PipeReader();
|
mPipeReader = new PipeReader();
|
||||||
}
|
}
|
||||||
@ -126,6 +129,58 @@ public class PebbleLESupport {
|
|||||||
mMTU = Math.min(mtu, mMTULimit);
|
mMTU = Math.min(mtu, mMTULimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handlePPoGATTPacket(byte[] value) {
|
||||||
|
if (!mIsConnected) {
|
||||||
|
mIsConnected = true;
|
||||||
|
synchronized (this) {
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//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 (mPPAck != null) {
|
||||||
|
mPPAck.countDown();
|
||||||
|
} else {
|
||||||
|
LOG.warn("mPPAck countdownlatch is not present but it probably should");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (command == 0x02) { // some request?
|
||||||
|
LOG.info("got command 0x02");
|
||||||
|
if (value.length > 1) {
|
||||||
|
sendDataToPebble(new byte[]{0x03, 0x19, 0x19}); // no we don't know what that means
|
||||||
|
createPipedInputReader(); // FIXME: maybe not here
|
||||||
|
} else {
|
||||||
|
sendDataToPebble(new byte[]{0x03}); // no we don't know what that means
|
||||||
|
}
|
||||||
|
} else if (command == 0) { // normal package
|
||||||
|
LOG.info("got PPoGATT package serial = " + serial + " sending ACK");
|
||||||
|
|
||||||
|
sendAckToPebble(serial);
|
||||||
|
|
||||||
|
writeToPipedOutputStream(value, 1, value.length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendAckToPebble(int serial) {
|
||||||
|
if (mPebbleGATTServer != null) {
|
||||||
|
mPebbleGATTServer.sendAckToPebble(serial);
|
||||||
|
} else {
|
||||||
|
mPebbleGATTClient.sendAckToPebble(serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendDataToPebble(byte[] bytes) {
|
||||||
|
if (mPebbleGATTServer != null) {
|
||||||
|
mPebbleGATTServer.sendDataToPebble(bytes);
|
||||||
|
} else {
|
||||||
|
mPebbleGATTClient.sendDataToPebble(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class PipeReader extends Thread {
|
private class PipeReader extends Thread {
|
||||||
int mmSequence = 0;
|
int mmSequence = 0;
|
||||||
|
|
||||||
@ -159,7 +214,7 @@ public class PebbleLESupport {
|
|||||||
byte[] outBuf = new byte[chunkSize + 1];
|
byte[] outBuf = new byte[chunkSize + 1];
|
||||||
outBuf[0] = (byte) ((mmSequence++ << 3) & 0xff);
|
outBuf[0] = (byte) ((mmSequence++ << 3) & 0xff);
|
||||||
System.arraycopy(buf, srcPos, outBuf, 1, chunkSize);
|
System.arraycopy(buf, srcPos, outBuf, 1, chunkSize);
|
||||||
mPebbleGATTServer.sendDataToPebble(outBuf);
|
sendDataToPebble(outBuf);
|
||||||
srcPos += chunkSize;
|
srcPos += chunkSize;
|
||||||
payloadToSend -= chunkSize;
|
payloadToSend -= chunkSize;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user