diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java index a161508e..75ad9dc0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java @@ -17,6 +17,8 @@ import android.widget.Toast; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.Set; import java.util.UUID; @@ -182,22 +184,50 @@ public class BluetoothCommunicationService extends Service { } public void run() { - byte[] buffer = new byte[1000]; // buffer store for the stream - int bytes; // bytes returned from read() + byte[] buffer = new byte[8192]; + int bytes; while (true) { try { - byte[] ping = {0, 0, 0, 0}; - byte[] version = {0x00, 0x0d, 0x00, 0x11, 0x01, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72}; - // Read from the InputStream - //bytes = mmInStream.read(buffer,0,2); - //ByteBuffer buf = ByteBuffer.wrap(buffer); - //buf.order(ByteOrder.BIG_ENDIAN); - //short length = buf.getShort(); - //Log.e(TAG,Integer.toString(length+4) + " as total length"); - bytes = mmInStream.read(buffer); - Log.e(TAG, Integer.toString(bytes) + ": " + PebbleProtocol.decodeResponse(buffer)); - //write(version); + bytes = mmInStream.read(buffer, 0, 4); + if (bytes < 4) + continue; + + ByteBuffer buf = ByteBuffer.wrap(buffer); + buf.order(ByteOrder.BIG_ENDIAN); + short length = buf.getShort(); + short endpoint = buf.getShort(); + if (length < 0 || length > 8192) { + Log.i(TAG, "invalid length " + length); + while (mmInStream.available() > 0) { + mmInStream.read(buffer); // read all + } + continue; + } + + bytes = mmInStream.read(buffer, 4, length); + if (bytes < length) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Log.i(TAG, "Read " + bytes + ", expected " + length + " reading remaining " + (length - bytes)); + int bytes_rest = mmInStream.read(buffer, 4 + bytes, length - bytes); + bytes += bytes_rest; + } + + if (length == 1 && endpoint == PebbleProtocol.ENDPOINT_PHONEVERSION) { + Log.i(TAG, "Pebble asked for Phone/App Version - repLYING!"); + write(PebbleProtocol.encodePhoneVersion(PebbleProtocol.PHONEVERSION_REMOTE_OS_ANDROID)); + } else { + Log.i(TAG, "unhandled message to endpoint " + endpoint + " (" + bytes + " bytes)"); + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } } catch (IOException e) { } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/PebbleProtocol.java index 389461fe..a5d4f125 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/PebbleProtocol.java @@ -44,11 +44,32 @@ public class PebbleProtocol { static final byte PHONECONTROL_START = 8; static final byte PHONECONTROL_END = 9; + static final short LENGTH_PREFIX = 4; + static final short LENGTH_SETTIME = 9; + static final short LENGTH_PHONEVERSION = 17; + static final byte TIME_GETTIME = 0; static final byte TIME_SETTIME = 2; - static final byte LENGTH_PREFIX = 4; - static final byte LENGTH_SETTIME = 9; + static final byte PHONEVERSION_APPVERSION = 2; // increase this if pebble complains + + static final int PHONEVERSION_SESSION_CAPS_GAMMARAY = (int) 0x80000000; + + static final int PHONEVERSION_REMOTE_CAPS_TELEPHONY = 0x00000010; + static final int PHONEVERSION_REMOTE_CAPS_SMS = 0x00000020; + static final int PHONEVERSION_REMOTE_CAPS_GPS = 0x00000040; + static final int PHONEVERSION_REMOTE_CAPS_BTLE = 0x00000080; + static final int PHONEVERSION_REMOTE_CAPS_REARCAMERA = 0x00000100; + static final int PHONEVERSION_REMOTE_CAPS_ACCEL = 0x00000200; + static final int PHONEVERSION_REMOTE_CAPS_GYRO = 0x00000400; + static final int PHONEVERSION_REMOTE_CAPS_COMPASS = 0x00000800; + + static final byte PHONEVERSION_REMOTE_OS_UNKNOWN = 0; + static final byte PHONEVERSION_REMOTE_OS_IOS = 1; + static final byte PHONEVERSION_REMOTE_OS_ANDROID = 2; + static final byte PHONEVERSION_REMOTE_OS_OSX = 3; + static final byte PHONEVERSION_REMOTE_OS_LINUX = 4; + static final byte PHONEVERSION_REMOTE_OS_WINDOWS = 5; static byte[] encodeMessage(short endpoint, byte type, String[] parts) { // Calculate length first @@ -78,6 +99,7 @@ public class PebbleProtocol { public static byte[] encodeSMS(String from, String body) { Long ts = System.currentTimeMillis() / 1000; + ts += SimpleTimeZone.getDefault().getOffset(ts) / 1000; String tsstring = ts.toString(); // SIC String[] parts = {from, body, tsstring}; @@ -86,6 +108,7 @@ public class PebbleProtocol { public static byte[] encodeEmail(String from, String subject, String body) { Long ts = System.currentTimeMillis() / 1000; + ts += SimpleTimeZone.getDefault().getOffset(ts) / 1000; String tsstring = ts.toString(); // SIC String[] parts = {from, body, tsstring, subject}; @@ -113,6 +136,25 @@ public class PebbleProtocol { return encodeMessage(ENDPOINT_PHONECONTROL, PHONECONTROL_INCOMINGCALL, parts); } + public static byte[] encodePhoneVersion(byte os) { + ByteBuffer buf = ByteBuffer.allocate(LENGTH_PHONEVERSION); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putShort((short) (LENGTH_PHONEVERSION - LENGTH_PREFIX)); + buf.putShort(ENDPOINT_PHONEVERSION); + buf.put(PHONEVERSION_APPVERSION); + + buf.putInt(-1); // TODO: Find out what this is (cookie?) + + if (os == PHONEVERSION_REMOTE_OS_ANDROID) { + buf.putInt(PHONEVERSION_SESSION_CAPS_GAMMARAY); + } else { + buf.putInt(0); + } + buf.putInt(PHONEVERSION_REMOTE_CAPS_SMS | PHONEVERSION_REMOTE_CAPS_TELEPHONY | os); + + return buf.array(); + } + // FIXME: that should return data into some unified struct/Class public static String decodeResponse(byte[] responseData) { ByteBuffer buf = ByteBuffer.wrap(responseData);