diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java new file mode 100644 index 00000000..971d99cc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java @@ -0,0 +1,16 @@ +package nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble; + +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; + +public class GBDeviceEventDataLogging extends GBDeviceEvent { + public static final int COMMAND_RECEIVE_DATA = 1; + public static final int COMMAND_FINISH_SESSION = 2; + + public int command; + public UUID appUUID; + public long tag; + public byte pebbleDataType; + public Object data; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java index 2b07d944..fc729143 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java @@ -1,9 +1,17 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.nio.ByteBuffer; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; + class DatalogSession { + private static final Logger LOG = LoggerFactory.getLogger(DatalogSession.class); + final byte id; final int tag; final UUID uuid; @@ -26,4 +34,38 @@ class DatalogSession { String getTaginfo() { return taginfo; } + + GBDeviceEvent[] handleMessageForPebbleKit(ByteBuffer buf, int length) { + if (0 != (length % itemSize)) { + LOG.warn("invalid length"); + return null; + } + int packetCount = length / itemSize; + GBDeviceEvent[] gbDeviceEvents = new GBDeviceEvent[packetCount + 1]; // pad for ack + for (int i = 0; i < packetCount; i++) { + GBDeviceEventDataLogging dataLogging = new GBDeviceEventDataLogging(); + switch (itemType) { + case PebbleProtocol.TYPE_BYTEARRAY: + byte[] itemData = new byte[itemSize]; + buf.get(itemData); + dataLogging.data = itemData; + break; + + case PebbleProtocol.TYPE_UINT: + dataLogging.data = buf.getInt() & 0xffffffffL; + break; + + case PebbleProtocol.TYPE_INT: + dataLogging.data = buf.getInt(); + break; + } + + dataLogging.command = GBDeviceEventDataLogging.COMMAND_RECEIVE_DATA; + dataLogging.appUUID = uuid; + dataLogging.tag = tag; + dataLogging.pebbleDataType = itemType; + gbDeviceEvents[i] = dataLogging; + } + return gbDeviceEvents; + } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 35c1c256..4ec9cacb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppMessage; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PBWReader; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleInstallable; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -86,6 +87,7 @@ class PebbleIoThread extends GBDeviceIoThread { mPebbleSupport = pebbleSupport; mEnablePebblekit = prefs.getBoolean("pebble_enable_pebblekit", false); mPebbleProtocol.setAlwaysACKPebbleKit(prefs.getBoolean("pebble_always_ack_pebblekit", false)); + mPebbleProtocol.setEnablePebbleKit(mEnablePebblekit); } private int readWithException(InputStream inputStream, byte[] buffer, int byteOffset, int byteCount) throws IOException { @@ -493,6 +495,13 @@ class PebbleIoThread extends GBDeviceIoThread { mPebbleKitSupport.sendAppMessageIntent((GBDeviceEventAppMessage) deviceEvent); } } + } else if (deviceEvent instanceof GBDeviceEventDataLogging) { + if (mEnablePebblekit) { + LOG.info("Got Datalogging event"); + if (mPebbleKitSupport != null) { + mPebbleKitSupport.sendDataLoggingIntent((GBDeviceEventDataLogging) deviceEvent); + } + } } return false; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java index 44117004..9f285de8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.util.Base64; import org.json.JSONArray; import org.json.JSONException; @@ -13,6 +14,7 @@ import org.slf4j.LoggerFactory; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppMessage; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; class PebbleKitSupport { //private static final String PEBBLEKIT_ACTION_PEBBLE_CONNECTED = "com.getpebble.action.PEBBLE_CONNECTED"; @@ -26,12 +28,20 @@ class PebbleKitSupport { private static final String PEBBLEKIT_ACTION_APP_START = "com.getpebble.action.app.START"; private static final String PEBBLEKIT_ACTION_APP_STOP = "com.getpebble.action.app.STOP"; + private static final String PEBBLEKIT_ACTION_DL_RECEIVE_DATA_NEW = "com.getpebble.action.dl.RECEIVE_DATA_NEW"; + private static final String PEBBLEKIT_ACTION_DL_RECEIVE_DATA = "com.getpebble.action.dl.RECEIVE_DATA"; + private static final String PEBBLEKIT_ACTION_DL_ACK_DATA = "com.getpebble.action.dl.ACK_DATA"; + private static final String PEBBLEKIT_ACTION_DL_REQUEST_DATA = "com.getpebble.action.dl.REQUEST_DATA"; + private static final String PEBBLEKIT_ACTION_DL_FINISH_SESSION = "com.getpebble.action.dl.FINISH_SESSION_NEW"; + private static final Logger LOG = LoggerFactory.getLogger(PebbleKitSupport.class); private final PebbleProtocol mPebbleProtocol; private final Context mContext; private final PebbleIoThread mPebbleIoThread; + private int dataLogTransactionId = 1; + private final BroadcastReceiver mPebbleKitReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -72,6 +82,9 @@ class PebbleKitSupport { } } break; + case PEBBLEKIT_ACTION_DL_ACK_DATA: + LOG.info("GOT DL DATA ACK"); + break; } } @@ -88,6 +101,7 @@ class PebbleKitSupport { intentFilter.addAction(PEBBLEKIT_ACTION_APP_SEND); intentFilter.addAction(PEBBLEKIT_ACTION_APP_START); intentFilter.addAction(PEBBLEKIT_ACTION_APP_STOP); + intentFilter.addAction(PEBBLEKIT_ACTION_DL_ACK_DATA); mContext.registerReceiver(mPebbleKitReceiver, intentFilter); } @@ -116,4 +130,40 @@ class PebbleKitSupport { } } + void sendDataLoggingIntent(GBDeviceEventDataLogging dataLogging) { + Intent intent = new Intent(); + intent.putExtra("data_log_timestamp", System.currentTimeMillis() / 1000); // is this data really not present in data from watch?! + intent.putExtra("uuid", dataLogging.appUUID); + intent.putExtra("data_log_uuid", dataLogging.appUUID); // Is that really the same? + intent.putExtra("data_log_tag", dataLogging.tag); + + switch (dataLogging.command) { + case GBDeviceEventDataLogging.COMMAND_RECEIVE_DATA: + intent.setAction(PEBBLEKIT_ACTION_DL_RECEIVE_DATA_NEW); + intent.putExtra("pbl_data_id", dataLogTransactionId++); + intent.putExtra("pbl_data_type", dataLogging.pebbleDataType); + switch (dataLogging.pebbleDataType) { + case PebbleProtocol.TYPE_BYTEARRAY: + intent.putExtra("pbl_data_object", Base64.encodeToString((byte[]) dataLogging.data, Base64.NO_WRAP)); + break; + case PebbleProtocol.TYPE_UINT: + intent.putExtra("pbl_data_object", (Long) dataLogging.data); + break; + case PebbleProtocol.TYPE_INT: + intent.putExtra("pbl_data_object", (Integer) dataLogging.data); + break; + } + LOG.info("broadcasting datalogging to uuid " + dataLogging.appUUID + " tag: " + dataLogging.tag + "transaction id: " + dataLogTransactionId + " type: " + dataLogging.pebbleDataType); + break; + case GBDeviceEventDataLogging.COMMAND_FINISH_SESSION: + intent.setAction(PEBBLEKIT_ACTION_DL_FINISH_SESSION); + LOG.info("broadcasting datalogging finish session to uuid " + dataLogging.appUUID + " tag: " + dataLogging.tag); + + break; + default: + LOG.warn("invalid datalog command"); + return; + } + mContext.sendBroadcast(intent); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index f3ae7825..ce252975 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -28,6 +28,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificati import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; @@ -224,10 +225,10 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final byte PHONEVERSION_REMOTE_OS_LINUX = 4; private static final byte PHONEVERSION_REMOTE_OS_WINDOWS = 5; - private static final byte TYPE_BYTEARRAY = 0; + static final byte TYPE_BYTEARRAY = 0; private static final byte TYPE_CSTRING = 1; - private static final byte TYPE_UINT = 2; - private static final byte TYPE_INT = 3; + static final byte TYPE_UINT = 2; + static final byte TYPE_INT = 3; private final short LENGTH_PREFIX = 4; @@ -253,6 +254,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final Random mRandom = new Random(); int mFwMajor = 3; + boolean mEnablePebbleKit = false; boolean mAlwaysACKPebbleKit = false; private boolean mForceProtocol = false; private GBDeviceEventScreenshot mDevEventScreenshot = null; @@ -2212,10 +2214,11 @@ public class PebbleProtocol extends GBDeviceProtocol { return null; } - private GBDeviceEventSendBytes decodeDatalog(ByteBuffer buf, short length) { + private GBDeviceEvent[] decodeDatalog(ByteBuffer buf, short length) { boolean ack = true; byte command = buf.get(); byte id = buf.get(); + GBDeviceEvent[] devEvts = new GBDeviceEvent[1]; switch (command) { case DATALOG_TIMEOUT: LOG.info("DATALOG TIMEOUT. id=" + (id & 0xff) + " - ignoring"); @@ -2228,7 +2231,11 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("DATALOG SENDDATA. id=" + (id & 0xff) + ", items_left=" + items_left + ", total length=" + (length - 10)); if (datalogSession != null) { LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + datalogSession.getTaginfo() + ", itemSize=" + datalogSession.itemSize + ", itemType=" + datalogSession.itemType); - ack = datalogSession.handleMessage(buf, length - 10); + if (!datalogSession.uuid.equals(UUID_ZERO) && datalogSession.getClass().equals(DatalogSession.class) && mEnablePebbleKit) { + devEvts = datalogSession.handleMessageForPebbleKit(buf, length - 10); + } else { + ack = datalogSession.handleMessage(buf, length - 10); + } } break; case DATALOG_OPENSESSION: @@ -2255,7 +2262,15 @@ public class PebbleProtocol extends GBDeviceProtocol { break; case DATALOG_CLOSE: LOG.info("DATALOG_CLOSE. id=" + (id & 0xff)); - if (mDatalogSessions.containsKey(id)) { + datalogSession = mDatalogSessions.get(id); + if (datalogSession != null) { + if (!datalogSession.uuid.equals(UUID_ZERO) && datalogSession.getClass().equals(DatalogSession.class) && mEnablePebbleKit) { + GBDeviceEventDataLogging dataLogging = new GBDeviceEventDataLogging(); + dataLogging.command = GBDeviceEventDataLogging.COMMAND_FINISH_SESSION; + dataLogging.appUUID = datalogSession.uuid; + dataLogging.tag = datalogSession.tag; + devEvts = new GBDeviceEvent[]{dataLogging, null}; + } mDatalogSessions.remove(id); } break; @@ -2271,7 +2286,9 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("sending NACK (0x86)"); sendBytes.encodedBytes = encodeDatalog(id, DATALOG_NACK); } - return sendBytes; + // append ack/nack + devEvts[devEvts.length - 1] = sendBytes; + return devEvts; } private GBDeviceEvent decodeAppReorder(ByteBuffer buf) { @@ -2539,7 +2556,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } break; case ENDPOINT_DATALOG: - devEvts = new GBDeviceEvent[]{decodeDatalog(buf, length)}; + devEvts = decodeDatalog(buf, length); break; case ENDPOINT_SCREENSHOT: devEvts = new GBDeviceEvent[]{decodeScreenshot(buf, length)}; @@ -2587,10 +2604,15 @@ public class PebbleProtocol extends GBDeviceProtocol { } void setAlwaysACKPebbleKit(boolean alwaysACKPebbleKit) { - LOG.info("setting always ACK Pebbleit to " + alwaysACKPebbleKit); + LOG.info("setting always ACK PebbleKit to " + alwaysACKPebbleKit); mAlwaysACKPebbleKit = alwaysACKPebbleKit; } + void setEnablePebbleKit(boolean enablePebbleKit) { + LOG.info("setting enable PebbleKit support to " + enablePebbleKit); + mEnablePebbleKit = enablePebbleKit; + } + private String getFixedString(ByteBuffer buf, int length) { byte[] tmp = new byte[length]; buf.get(tmp, 0, length);