CMF Watch Pro: Firmware update

Tested by updating to 11.0.0.57
This commit is contained in:
José Rebelo
2025-06-27 00:05:18 +01:00
parent d0b85358b6
commit 533ecb9ee3
5 changed files with 142 additions and 20 deletions
@@ -473,8 +473,10 @@ public class BleNamesResolver {
mServices.put("16187f00-0000-1000-8000-00807f9b34fb", "(Propr: Xiaomi Wear Service - Mi Smart Watch 4C/Redmi Band)"); mServices.put("16187f00-0000-1000-8000-00807f9b34fb", "(Propr: Xiaomi Wear Service - Mi Smart Watch 4C/Redmi Band)");
mServices.put("1314f000-1000-9000-7000-301291e21220", "(Propr: Xiaomi Wear Service - Mi Watch/Mi Watch Color/Mi Watch Color Sport)"); mServices.put("1314f000-1000-9000-7000-301291e21220", "(Propr: Xiaomi Wear Service - Mi Watch/Mi Watch Color/Mi Watch Color Sport)");
mServices.put("7495fe00-a7f3-424b-92dd-4a006a3aef56", "(Propr: Xiaomi Wear Service - Mi Watch CN)"); mServices.put("7495fe00-a7f3-424b-92dd-4a006a3aef56", "(Propr: Xiaomi Wear Service - Mi Watch CN)");
//mServices.put("0000fff0-0000-1000-8000-00805f9b34fb", "(Propr: Nothing CMF Command"); mServices.put("0000fff0-0000-1000-8000-00805f9b34fb", "(Propr: Nothing CMF Command");
//mServices.put("02f00000-0000-0000-0000-00000000ffe0", "(Propr: Nothing CMF Data"); mServices.put("02f00000-0000-0000-0000-00000000ffe0", "(Propr: Nothing CMF Data");
mServices.put("02f00000-0000-0000-0000-00000000fe00", "(Propr: Nothing CMF Firmware");
mServices.put("77d4e67c-2fe2-2334-0d35-9ccd078f529c", "(Propr: Nothing CMF Shell");
mServices.put("9b012401-bc30-ce9a-e111-0f67e491abde", "(Propr: Garmin GFDI V0)"); mServices.put("9b012401-bc30-ce9a-e111-0f67e491abde", "(Propr: Garmin GFDI V0)");
mServices.put("6a4e2401-667b-11e3-949a-0800200c9a66", "(Propr: Garmin GFDI V1)"); mServices.put("6a4e2401-667b-11e3-949a-0800200c9a66", "(Propr: Garmin GFDI V1)");
mServices.put("6a4e2800-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML)"); mServices.put("6a4e2800-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML)");
@@ -972,18 +974,22 @@ public class BleNamesResolver {
mCharacteristics.put("6a4e2823-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML 3 TX)"); mCharacteristics.put("6a4e2823-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML 3 TX)");
mCharacteristics.put("6a4e2814-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML 4 RX)"); mCharacteristics.put("6a4e2814-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML 4 RX)");
mCharacteristics.put("6a4e2824-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML 4 TX)"); mCharacteristics.put("6a4e2824-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML 4 TX)");
//mCharacteristics.put("0000fff1-0000-1000-8000-00805f9b34fb", "(Propr: Nothing CMF Command Read"); mCharacteristics.put("0000fff1-0000-1000-8000-00805f9b34fb", "(Propr: Nothing CMF Command Read");
//mCharacteristics.put("0000fff2-0000-1000-8000-00805f9b34fb", "(Propr: Nothing CMF Command Write"); mCharacteristics.put("0000fff2-0000-1000-8000-00805f9b34fb", "(Propr: Nothing CMF Command Write");
//mCharacteristics.put("02f00000-0000-0000-0000-00000000ffe1", "(Propr: Nothing CMF Data Write"); mCharacteristics.put("02f00000-0000-0000-0000-00000000ffe1", "(Propr: Nothing CMF Data Write");
//mCharacteristics.put("02f00000-0000-0000-0000-00000000ffe2", "(Propr: Nothing CMF Data Read"); mCharacteristics.put("02f00000-0000-0000-0000-00000000ffe2", "(Propr: Nothing CMF Data Read");
//mCharacteristics.put("00010203-0405-0607-0809-0a0b0c0d2b12", "(Propr: Telink OTA Write)"); mCharacteristics.put("77d4ff01-2fe2-2334-0d35-9ccd078f529c", "(Propr: Nothing CMF Shell Write");
//mCharacteristics.put("ebe0ccb7-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd TIME)"); mCharacteristics.put("77d4ff02-2fe2-2334-0d35-9ccd078f529c", "(Propr: Nothing CMF Shell Read");
//mCharacteristics.put("ebe0ccc4-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd BATTERY)"); mCharacteristics.put("02f00000-0000-0000-0000-00000000ff01", "(Propr: Nothing CMF Firmware Write");
//mCharacteristics.put("ebe0ccbe-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd TEMPERATURE_UNIT)"); mCharacteristics.put("02f00000-0000-0000-0000-00000000ff02", "(Propr: Nothing CMF Firmware Read");
//mCharacteristics.put("ebe0ccd8-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd CONN_INTERVAL)"); mCharacteristics.put("00010203-0405-0607-0809-0a0b0c0d2b12", "(Propr: Telink OTA Write)");
//mCharacteristics.put("ebe0ccbc-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd HISTORY)"); mCharacteristics.put("ebe0ccb7-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd TIME)");
//mCharacteristics.put("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd LIVE_DATA)"); mCharacteristics.put("ebe0ccc4-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd BATTERY)");
//mCharacteristics.put("ebe0ccba-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd HISTORY_LAST_ID)"); mCharacteristics.put("ebe0ccbe-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd TEMPERATURE_UNIT)");
mCharacteristics.put("ebe0ccd8-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd CONN_INTERVAL)");
mCharacteristics.put("ebe0ccbc-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd HISTORY)");
mCharacteristics.put("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd LIVE_DATA)");
mCharacteristics.put("ebe0ccba-7a0a-4b0c-8a1a-6ff2997da3a6", "(Propr: Lywsd HISTORY_LAST_ID)");
mCharacteristics.put("00000001-0000-3512-2118-0009af100700", "(Propr: Huami Raw Sensor Control)"); mCharacteristics.put("00000001-0000-3512-2118-0009af100700", "(Propr: Huami Raw Sensor Control)");
mCharacteristics.put("00000002-0000-3512-2118-0009af100700", "(Propr: Huami Raw Sensor Data)"); mCharacteristics.put("00000002-0000-3512-2118-0009af100700", "(Propr: Huami Raw Sensor Data)");
mCharacteristics.put("00000003-0000-3512-2118-0009af100700", "(Propr: Huami Configuration)"); mCharacteristics.put("00000003-0000-3512-2118-0009af100700", "(Propr: Huami Configuration)");
@@ -185,6 +185,7 @@ public class CmfCharacteristic {
case AUTH_PAIR_REQUEST: case AUTH_PAIR_REQUEST:
case AUTH_PAIR_REPLY: case AUTH_PAIR_REPLY:
case DATA_CHUNK_WRITE_AGPS: case DATA_CHUNK_WRITE_AGPS:
case DATA_CHUNK_WRITE_FIRMWARE:
case DATA_CHUNK_WRITE_WATCHFACE: case DATA_CHUNK_WRITE_WATCHFACE:
return false; return false;
} }
@@ -42,13 +42,21 @@ public enum CmfCommand {
CONTACTS_GET(0x00d5, 0x0002), CONTACTS_GET(0x00d5, 0x0002),
CONTACTS_SET(0x00d5, 0x0001), CONTACTS_SET(0x00d5, 0x0001),
DATA_CHUNK_REQUEST_AGPS(0xffff, 0xa05f), DATA_CHUNK_REQUEST_AGPS(0xffff, 0xa05f),
DATA_CHUNK_REQUEST_FIRMWARE(0xffff, 0xa042),
DATA_CHUNK_REQUEST_WATCHFACE(0xffff, 0xa064), DATA_CHUNK_REQUEST_WATCHFACE(0xffff, 0xa064),
DATA_CHUNK_WRITE_AGPS(0xffff, 0x905f), DATA_CHUNK_WRITE_AGPS(0xffff, 0x905f),
DATA_CHUNK_WRITE_FIRMWARE(0xffff, 0x9042),
DATA_CHUNK_WRITE_WATCHFACE(0xffff, 0x9064), DATA_CHUNK_WRITE_WATCHFACE(0xffff, 0x9064),
DATA_TRANSFER_AGPS_FINISH_ACK_1(0xffff, 0xa060), DATA_TRANSFER_AGPS_FINISH_ACK_1(0xffff, 0xa060),
DATA_TRANSFER_AGPS_FINISH_ACK_2(0xffff, 0x9060), DATA_TRANSFER_AGPS_FINISH_ACK_2(0xffff, 0x9060),
DATA_TRANSFER_AGPS_INIT_REPLY(0xffff, 0xa05e), DATA_TRANSFER_AGPS_INIT_REPLY(0xffff, 0xa05e),
DATA_TRANSFER_AGPS_INIT_REQUEST(0xffff, 0x905e), DATA_TRANSFER_AGPS_INIT_REQUEST(0xffff, 0x905e),
DATA_TRANSFER_FIRMWARE_INIT_1_REQUEST(0xffff, 0x9052),
DATA_TRANSFER_FIRMWARE_INIT_1_REPLY(0xffff, 0xa052),
DATA_TRANSFER_FIRMWARE_INIT_2_REQUEST(0xffff, 0x9040),
DATA_TRANSFER_FIRMWARE_INIT_2_REPLY(0xffff, 0xa040),
DATA_TRANSFER_FIRMWARE_FINISH_ACK_1(0xffff, 0xa041),
DATA_TRANSFER_FIRMWARE_FINISH_ACK_2(0xffff, 0x9041),
DATA_TRANSFER_WATCHFACE_FINISH_ACK_1(0xffff, 0xa065), DATA_TRANSFER_WATCHFACE_FINISH_ACK_1(0xffff, 0xa065),
DATA_TRANSFER_WATCHFACE_FINISH_ACK_2(0xffff, 0x9065), DATA_TRANSFER_WATCHFACE_FINISH_ACK_2(0xffff, 0x9065),
DATA_TRANSFER_WATCHFACE_INIT_1_REQUEST(0xffff, 0x8052), DATA_TRANSFER_WATCHFACE_INIT_1_REQUEST(0xffff, 0x8052),
@@ -45,7 +45,7 @@ public class CmfDataUploader implements CmfCharacteristic.Handler {
@Override @Override
public void onCommand(final CmfCommand cmd, final byte[] payload) { public void onCommand(final CmfCommand cmd, final byte[] payload) {
switch (cmd) { switch (cmd) {
case DATA_TRANSFER_WATCHFACE_INIT_1_REPLY: case DATA_TRANSFER_WATCHFACE_INIT_1_REPLY: {
if (payload[0] != 0x01) { if (payload[0] != 0x01) {
LOG.warn("Got unexpected transfer init 1 reply {}", payload[0]); LOG.warn("Got unexpected transfer init 1 reply {}", payload[0]);
fwHelper = null; fwHelper = null;
@@ -63,7 +63,30 @@ public class CmfDataUploader implements CmfCharacteristic.Handler {
buf.array() buf.array()
); );
return; return;
}
case DATA_TRANSFER_FIRMWARE_INIT_1_REPLY: {
if (payload[0] != 0x01) {
LOG.warn("Got unexpected firmware init 2 reply {}", payload[0]);
fwHelper = null;
return;
}
final ByteBuffer buf = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
// FIXME version a.b.c.d... how to know? this was from 11.0.0.57
buf.put((byte) (0x0b));
buf.put((byte) (0x00));
buf.put((byte) (0x00));
buf.put((byte) (0x39));
mSupport.sendFirmware(
"transfer firmware init request",
CmfCommand.DATA_TRANSFER_FIRMWARE_INIT_2_REQUEST,
buf.array()
);
return;
}
case DATA_TRANSFER_AGPS_INIT_REPLY: case DATA_TRANSFER_AGPS_INIT_REPLY:
case DATA_TRANSFER_FIRMWARE_INIT_2_REPLY:
case DATA_TRANSFER_WATCHFACE_INIT_2_REPLY: case DATA_TRANSFER_WATCHFACE_INIT_2_REPLY:
if (payload[0] != 0x01) { if (payload[0] != 0x01) {
LOG.warn("Got unexpected transfer 2 init reply {}", payload[0]); LOG.warn("Got unexpected transfer 2 init reply {}", payload[0]);
@@ -78,6 +101,11 @@ public class CmfDataUploader implements CmfCharacteristic.Handler {
case DATA_TRANSFER_WATCHFACE_FINISH_ACK_1: case DATA_TRANSFER_WATCHFACE_FINISH_ACK_1:
handleAck1(CmfCommand.DATA_TRANSFER_WATCHFACE_FINISH_ACK_2, payload); handleAck1(CmfCommand.DATA_TRANSFER_WATCHFACE_FINISH_ACK_2, payload);
return; return;
case DATA_TRANSFER_FIRMWARE_FINISH_ACK_1:
// TODO: Confirm if this is being sent in the right characteristic, although it looks
// like it does not matter, since it restarts right away
handleAck1(CmfCommand.DATA_TRANSFER_FIRMWARE_FINISH_ACK_2, payload);
return;
case DATA_TRANSFER_AGPS_FINISH_ACK_1: case DATA_TRANSFER_AGPS_FINISH_ACK_1:
handleAck1(CmfCommand.DATA_TRANSFER_AGPS_FINISH_ACK_2, payload); handleAck1(CmfCommand.DATA_TRANSFER_AGPS_FINISH_ACK_2, payload);
return; return;
@@ -95,6 +123,13 @@ public class CmfDataUploader implements CmfCharacteristic.Handler {
} }
handleChunkRequest(CmfCommand.DATA_CHUNK_WRITE_WATCHFACE, payload); handleChunkRequest(CmfCommand.DATA_CHUNK_WRITE_WATCHFACE, payload);
return; return;
case DATA_CHUNK_REQUEST_FIRMWARE:
if (fwHelper == null || !fwHelper.isFirmware()) {
LOG.warn("We are not sending firmware - refusing request");
return;
}
handleChunkRequest(CmfCommand.DATA_CHUNK_WRITE_FIRMWARE, payload);
return;
} }
LOG.warn("Got unknown data command {}", cmd); LOG.warn("Got unknown data command {}", cmd);
@@ -123,6 +158,18 @@ public class CmfDataUploader implements CmfCharacteristic.Handler {
return; return;
} }
/* FIXME: This is disabled until we figure out how to send the firmware version
if (fwHelper.isFirmware()) {
mSupport.sendCommand(
"transfer firmware init request",
CmfCommand.DATA_TRANSFER_FIRMWARE_INIT_1_REQUEST,
(byte) 0xa5
);
return;
}
*/
LOG.warn("Unsupported fwHelper for {}", fwHelper.getUri()); LOG.warn("Unsupported fwHelper for {}", fwHelper.getUri());
fwHelper = null; fwHelper = null;
} }
@@ -137,11 +184,19 @@ public class CmfDataUploader implements CmfCharacteristic.Handler {
final TransactionBuilder builder = mSupport.createTransactionBuilder("send chunk offset " + offset); final TransactionBuilder builder = mSupport.createTransactionBuilder("send chunk offset " + offset);
updateProgress(builder, progress, true); updateProgress(builder, progress, true);
mSupport.sendData( if (commandReply == CmfCommand.DATA_CHUNK_WRITE_FIRMWARE) {
"transfer watchface init request", mSupport.sendFirmware(
commandReply, "send firmware chunk",
ArrayUtils.subarray(fwHelper.getBytes(), offset, offset + length) commandReply,
); ArrayUtils.subarray(fwHelper.getBytes(), offset, offset + length)
);
} else {
mSupport.sendData(
"send data chunk",
commandReply,
ArrayUtils.subarray(fwHelper.getBytes(), offset, offset + length)
);
}
} }
private void handleAck1(final CmfCommand commandReply, final byte[] payload) { private void handleAck1(final CmfCommand commandReply, final byte[] payload) {
@@ -94,6 +94,10 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
public static final UUID UUID_CHARACTERISTIC_CMF_SHELL_WRITE = UUID.fromString("77d4ff01-2fe2-2334-0d35-9ccd078f529c"); public static final UUID UUID_CHARACTERISTIC_CMF_SHELL_WRITE = UUID.fromString("77d4ff01-2fe2-2334-0d35-9ccd078f529c");
public static final UUID UUID_CHARACTERISTIC_CMF_SHELL_READ = UUID.fromString("77d4ff02-2fe2-2334-0d35-9ccd078f529c"); public static final UUID UUID_CHARACTERISTIC_CMF_SHELL_READ = UUID.fromString("77d4ff02-2fe2-2334-0d35-9ccd078f529c");
public static final UUID UUID_SERVICE_CMF_FIRMWARE = UUID.fromString("02f00000-0000-0000-0000-00000000fe00");
public static final UUID UUID_CHARACTERISTIC_CMF_FIRMWARE_WRITE = UUID.fromString("02f00000-0000-0000-0000-00000000ff01");
public static final UUID UUID_CHARACTERISTIC_CMF_FIRMWARE_READ = UUID.fromString("02f00000-0000-0000-0000-00000000ff02");
// An a5 byte is used a lot in single payloads, probably as a "proof of encryption"? // An a5 byte is used a lot in single payloads, probably as a "proof of encryption"?
public static final byte A5 = (byte) 0xa5; public static final byte A5 = (byte) 0xa5;
@@ -101,6 +105,10 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
private CmfCharacteristic characteristicCommandWrite; private CmfCharacteristic characteristicCommandWrite;
private CmfCharacteristic characteristicDataRead; private CmfCharacteristic characteristicDataRead;
private CmfCharacteristic characteristicDataWrite; private CmfCharacteristic characteristicDataWrite;
@Nullable
private CmfCharacteristic characteristicFirmwareRead;
@Nullable
private CmfCharacteristic characteristicFirmwareWrite;
private final byte[] authRandom1 = new byte[16]; private final byte[] authRandom1 = new byte[16];
private final byte[] authAppSecret = new byte[16]; private final byte[] authAppSecret = new byte[16];
@@ -116,12 +124,14 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
addSupportedService(UUID_SERVICE_CMF_CMD); addSupportedService(UUID_SERVICE_CMF_CMD);
addSupportedService(UUID_SERVICE_CMF_DATA); addSupportedService(UUID_SERVICE_CMF_DATA);
addSupportedService(UUID_SERVICE_CMF_SHELL); addSupportedService(UUID_SERVICE_CMF_SHELL);
addSupportedService(UUID_SERVICE_CMF_FIRMWARE);
} }
@Override @Override
public boolean useAutoConnect() { public boolean useAutoConnect() {
return true; return true;
} }
@Override @Override
protected TransactionBuilder initializeDevice(final TransactionBuilder builder) { protected TransactionBuilder initializeDevice(final TransactionBuilder builder) {
builder.setUpdateState(getDevice(), GBDevice.State.INITIALIZING, getContext()); builder.setUpdateState(getDevice(), GBDevice.State.INITIALIZING, getContext());
@@ -164,18 +174,35 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
LOG.warn("Characteristic shell read is null"); LOG.warn("Characteristic shell read is null");
} }
final BluetoothGattCharacteristic btCharacteristicFirmwareWrite = getCharacteristic(UUID_CHARACTERISTIC_CMF_FIRMWARE_WRITE);
if (btCharacteristicFirmwareWrite == null) {
LOG.warn("Characteristic firmware write is null");
}
final BluetoothGattCharacteristic btCharacteristicFirmwareRead = getCharacteristic(UUID_CHARACTERISTIC_CMF_FIRMWARE_READ);
if (btCharacteristicFirmwareRead == null) {
LOG.warn("Characteristic firmware read is null");
}
dataUploader = new CmfDataUploader(this); dataUploader = new CmfDataUploader(this);
characteristicCommandRead = new CmfCharacteristic(btCharacteristicCommandRead, this); characteristicCommandRead = new CmfCharacteristic(btCharacteristicCommandRead, this);
characteristicCommandWrite = new CmfCharacteristic(btCharacteristicCommandWrite, null); characteristicCommandWrite = new CmfCharacteristic(btCharacteristicCommandWrite, null);
characteristicDataRead = new CmfCharacteristic(btCharacteristicDataRead, dataUploader); characteristicDataRead = new CmfCharacteristic(btCharacteristicDataRead, dataUploader);
characteristicDataWrite = new CmfCharacteristic(btCharacteristicDataWrite, null); characteristicDataWrite = new CmfCharacteristic(btCharacteristicDataWrite, null);
if (btCharacteristicFirmwareRead != null && btCharacteristicFirmwareWrite != null) {
characteristicFirmwareRead = new CmfCharacteristic(btCharacteristicFirmwareRead, dataUploader);
characteristicFirmwareWrite = new CmfCharacteristic(btCharacteristicFirmwareWrite, null);
}
builder.notify(btCharacteristicCommandRead, true); builder.notify(btCharacteristicCommandRead, true);
builder.notify(btCharacteristicDataRead, true); builder.notify(btCharacteristicDataRead, true);
if (btCharacteristicShellRead != null) { if (btCharacteristicShellRead != null) {
builder.notify(btCharacteristicShellRead, true); builder.notify(btCharacteristicShellRead, true);
} }
if (btCharacteristicFirmwareRead != null) {
builder.notify(btCharacteristicFirmwareRead, true);
}
builder.setUpdateState(getDevice(), GBDevice.State.AUTHENTICATING, getContext()); builder.setUpdateState(getDevice(), GBDevice.State.AUTHENTICATING, getContext());
@@ -186,6 +213,12 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
characteristicCommandWrite.setSessionKey(secretKey); characteristicCommandWrite.setSessionKey(secretKey);
characteristicDataRead.setSessionKey(secretKey); characteristicDataRead.setSessionKey(secretKey);
characteristicDataWrite.setSessionKey(secretKey); characteristicDataWrite.setSessionKey(secretKey);
if (characteristicFirmwareRead != null) {
characteristicFirmwareRead.setSessionKey(secretKey);
}
if (characteristicFirmwareWrite != null) {
characteristicFirmwareWrite.setSessionKey(secretKey);
}
sendCommand(builder, CmfCommand.AUTH_PHONE_NAME, ArrayUtils.addAll(new byte[]{A5}, Build.MODEL.getBytes(StandardCharsets.UTF_8))); sendCommand(builder, CmfCommand.AUTH_PHONE_NAME, ArrayUtils.addAll(new byte[]{A5}, Build.MODEL.getBytes(StandardCharsets.UTF_8)));
} else if (btCharacteristicShellWrite != null) { } else if (btCharacteristicShellWrite != null) {
@@ -221,6 +254,9 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
} else if (characteristicUUID.equals(characteristicDataRead.getCharacteristicUUID())) { } else if (characteristicUUID.equals(characteristicDataRead.getCharacteristicUUID())) {
characteristicDataRead.onCharacteristicChanged(value); characteristicDataRead.onCharacteristicChanged(value);
return true; return true;
} else if (characteristicFirmwareRead != null && characteristicUUID.equals(characteristicFirmwareRead.getCharacteristicUUID())) {
characteristicFirmwareRead.onCharacteristicChanged(value);
return true;
} else if (characteristicUUID.equals(UUID_CHARACTERISTIC_CMF_SHELL_READ)) { } else if (characteristicUUID.equals(UUID_CHARACTERISTIC_CMF_SHELL_READ)) {
handleShellCommand(value); handleShellCommand(value);
return true; return true;
@@ -255,6 +291,10 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
} }
switch (cmd) { switch (cmd) {
case DATA_TRANSFER_FIRMWARE_INIT_1_REPLY:
// This one comes in the command characteristic because of reasons
dataUploader.onCommand(cmd, payload);
return;
case AUTH_PAIR_REPLY: case AUTH_PAIR_REPLY:
final byte[] authRandom2 = ArrayUtils.subarray(payload, 0, 16); final byte[] authRandom2 = ArrayUtils.subarray(payload, 0, 16);
final byte[] signedAuthRandom2 = ArrayUtils.subarray(payload, 16, 48); final byte[] signedAuthRandom2 = ArrayUtils.subarray(payload, 16, 48);
@@ -326,6 +366,12 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
characteristicCommandWrite.setSessionKey(sessionKey); characteristicCommandWrite.setSessionKey(sessionKey);
characteristicDataRead.setSessionKey(sessionKey); characteristicDataRead.setSessionKey(sessionKey);
characteristicDataWrite.setSessionKey(sessionKey); characteristicDataWrite.setSessionKey(sessionKey);
if (characteristicFirmwareRead != null) {
characteristicFirmwareRead.setSessionKey(sessionKey);
}
if (characteristicFirmwareWrite != null) {
characteristicFirmwareWrite.setSessionKey(sessionKey);
}
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Failed to compute session key from auth nonce", e); LOG.error("Failed to compute session key from auth nonce", e);
return; return;
@@ -452,6 +498,12 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
builder.queue(getQueue()); builder.queue(getQueue());
} }
public void sendFirmware(final String taskName, final CmfCommand cmd, final byte... payload) {
final TransactionBuilder builder = createTransactionBuilder(taskName);
characteristicFirmwareWrite.sendCommand(builder, cmd, payload);
builder.queue(getQueue());
}
private void handleShellCommand(final byte[] bytes) { private void handleShellCommand(final byte[] bytes) {
final String shellCommand = new String(bytes).strip(); final String shellCommand = new String(bytes).strip();
if (!shellCommand.startsWith("GETSECRET:")) { if (!shellCommand.startsWith("GETSECRET:")) {