WIP: Lots of work towards double firmware update for Mi 1S #234
This commit is contained in:
parent
3f39928df5
commit
91f02ae920
|
@ -12,9 +12,15 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Also see Mi1SInfo.
|
||||||
|
*/
|
||||||
public class MiBandFWHelper {
|
public class MiBandFWHelper {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MiBandFWHelper.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MiBandFWHelper.class);
|
||||||
private static final int MI_FW_BASE_OFFSET = 1056;
|
private static final int MI_FW_BASE_OFFSET = 1056;
|
||||||
|
@ -117,10 +123,29 @@ public class MiBandFWHelper {
|
||||||
return (fw[getOffsetFirmwareVersionMajor()] << 24) | (fw[getOffsetFirmwareVersionMinor()] << 16) | (fw[getOffsetFirmwareVersionRevision()] << 8) | fw[getOffsetFirmwareVersionBuild()];
|
return (fw[getOffsetFirmwareVersionMajor()] << 24) | (fw[getOffsetFirmwareVersionMinor()] << 16) | (fw[getOffsetFirmwareVersionRevision()] << 8) | fw[getOffsetFirmwareVersionBuild()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String formatFirmwareVersion(int version) {
|
||||||
|
if (version == -1)
|
||||||
|
return GBApplication.getContext().getString(R.string._unknown_);
|
||||||
|
|
||||||
|
return String.format("%d.%d.%d.%d",
|
||||||
|
version >> 24 & 255,
|
||||||
|
version >> 16 & 255,
|
||||||
|
version >> 8 & 255,
|
||||||
|
version & 255);
|
||||||
|
}
|
||||||
|
|
||||||
public String getHumanFirmwareVersion() {
|
public String getHumanFirmwareVersion() {
|
||||||
return String.format(Locale.US, "%d.%d.%d.%d", fw[getOffsetFirmwareVersionMajor()], fw[getOffsetFirmwareVersionMinor()], fw[getOffsetFirmwareVersionRevision()], fw[getOffsetFirmwareVersionBuild()]);
|
return String.format(Locale.US, "%d.%d.%d.%d", fw[getOffsetFirmwareVersionMajor()], fw[getOffsetFirmwareVersionMinor()], fw[getOffsetFirmwareVersionRevision()], fw[getOffsetFirmwareVersionBuild()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getHumanFirmwareVersion2() {
|
||||||
|
return format(Mi1SInfo.getFirmware2VersionFrom(getFw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String format(int version) {
|
||||||
|
return formatFirmwareVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getFw() {
|
public byte[] getFw() {
|
||||||
return fw;
|
return fw;
|
||||||
}
|
}
|
||||||
|
@ -142,9 +167,13 @@ public class MiBandFWHelper {
|
||||||
if (MiBandConst.MI_1A.equals(deviceHW)) {
|
if (MiBandConst.MI_1A.equals(deviceHW)) {
|
||||||
return getFirmwareVersionMajor() == 5;
|
return getFirmwareVersionMajor() == 5;
|
||||||
}
|
}
|
||||||
// if (MiBandConst.MI_1S.equals(deviceHW)) {
|
if (true || MiBandConst.MI_1S.equals(deviceHW)) { // FIXME: REMOVE TEMPORARY HACK
|
||||||
// return getFirmwareVersionMajor() == 4;
|
return getFirmwareVersionMajor() == 4;
|
||||||
// }
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSingleFirmware() {
|
||||||
|
return Mi1SInfo.isSingleMiBandFirmware(getFw());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,13 @@ public class MiBandFWInstallHandler implements InstallHandler {
|
||||||
installActivity.setInstallEnabled(false);
|
installActivity.setInstallEnabled(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StringBuilder builder = new StringBuilder(mContext.getString(R.string.fw_upgrade_notice, helper.getHumanFirmwareVersion()));
|
StringBuilder builder = new StringBuilder();
|
||||||
|
if (helper.isSingleFirmware()) {
|
||||||
|
builder.append(mContext.getString(R.string.fw_upgrade_notice, helper.getHumanFirmwareVersion()));
|
||||||
|
} else {
|
||||||
|
builder.append(mContext.getString(R.string.fw_multi_upgrade_notice, helper.getHumanFirmwareVersion(), helper.getHumanFirmwareVersion2()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (helper.isFirmwareWhitelisted()) {
|
if (helper.isFirmwareWhitelisted()) {
|
||||||
builder.append(" ").append(mContext.getString(R.string.miband_firmware_known));
|
builder.append(" ").append(mContext.getString(R.string.miband_firmware_known));
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||||
|
|
||||||
public class DeviceInfo extends AbstractInfo {
|
public class DeviceInfo extends AbstractInfo {
|
||||||
public final String deviceId;
|
public final String deviceId;
|
||||||
public final int profileVersion;
|
public final int profileVersion;
|
||||||
|
/**
|
||||||
|
* Mi Band firmware version identifier
|
||||||
|
*/
|
||||||
public final int fwVersion;
|
public final int fwVersion;
|
||||||
public final int hwVersion;
|
public final int hwVersion;
|
||||||
public final int feature;
|
public final int feature;
|
||||||
public final int appearance;
|
public final int appearance;
|
||||||
|
/**
|
||||||
|
* Heart rate firmware version identifier
|
||||||
|
*/
|
||||||
|
public final int fw2Version;
|
||||||
|
|
||||||
|
|
||||||
private boolean isChecksumCorrect(byte[] data) {
|
private boolean isChecksumCorrect(byte[] data) {
|
||||||
|
@ -29,6 +35,15 @@ public class DeviceInfo extends AbstractInfo {
|
||||||
hwVersion = data[6] & 255;
|
hwVersion = data[6] & 255;
|
||||||
appearance = data[5] & 255;
|
appearance = data[5] & 255;
|
||||||
feature = data[4] & 255;
|
feature = data[4] & 255;
|
||||||
|
if (data.length == 20) {
|
||||||
|
int s = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
s |= (data[16 + i] & 255) << i * 8;
|
||||||
|
}
|
||||||
|
fw2Version = s;
|
||||||
|
} else {
|
||||||
|
fw2Version = -1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
deviceId = "crc error";
|
deviceId = "crc error";
|
||||||
profileVersion = -1;
|
profileVersion = -1;
|
||||||
|
@ -36,6 +51,7 @@ public class DeviceInfo extends AbstractInfo {
|
||||||
hwVersion = -1;
|
hwVersion = -1;
|
||||||
feature = -1;
|
feature = -1;
|
||||||
appearance = -1;
|
appearance = -1;
|
||||||
|
fw2Version = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,20 +68,21 @@ public class DeviceInfo extends AbstractInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHumanFirmwareVersion() {
|
public String getHumanFirmwareVersion() {
|
||||||
if (fwVersion == -1)
|
return MiBandFWHelper.formatFirmwareVersion(fwVersion);
|
||||||
return GBApplication.getContext().getString(R.string._unknown_);
|
}
|
||||||
|
|
||||||
return String.format("%d.%d.%d.%d",
|
public String getHumanFirmware2Version() {
|
||||||
fwVersion >> 24 & 255,
|
return MiBandFWHelper.formatFirmwareVersion(fw2Version);
|
||||||
fwVersion >> 16 & 255,
|
|
||||||
fwVersion >> 8 & 255,
|
|
||||||
fwVersion & 255);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFirmwareVersion() {
|
public int getFirmwareVersion() {
|
||||||
return fwVersion;
|
return fwVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getHeartrateFirmwareVersion() {
|
||||||
|
return fw2Version;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DeviceInfo{" +
|
return "DeviceInfo{" +
|
||||||
|
@ -75,6 +92,7 @@ public class DeviceInfo extends AbstractInfo {
|
||||||
", hwVersion=" + hwVersion +
|
", hwVersion=" + hwVersion +
|
||||||
", feature=" + feature +
|
", feature=" + feature +
|
||||||
", appearance=" + appearance +
|
", appearance=" + appearance +
|
||||||
|
", fw2Version (hr)=" + fw2Version+
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +117,8 @@ public class DeviceInfo extends AbstractInfo {
|
||||||
return MiBandConst.MI_1A;
|
return MiBandConst.MI_1A;
|
||||||
}
|
}
|
||||||
if (isMilli1S()) {
|
if (isMilli1S()) {
|
||||||
return MiBandConst.MI_1S;
|
return getHumanFirmware2Version();
|
||||||
|
// return MiBandConst.MI_1S;
|
||||||
}
|
}
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FW1 is Mi Band firmware
|
||||||
|
* FW2 is heartrate firmware
|
||||||
|
*/
|
||||||
|
public class Mi1SInfo {
|
||||||
|
|
||||||
|
public static int getFirmware2OffsetIn(byte[] wholeFirmwareBytes)
|
||||||
|
{
|
||||||
|
return (wholeFirmwareBytes[26] & 255) << 24
|
||||||
|
| (wholeFirmwareBytes[27] & 255) << 16
|
||||||
|
| (wholeFirmwareBytes[28] & 255) << 8
|
||||||
|
| (wholeFirmwareBytes[29] & 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getFirmware2LengthIn(byte[] wholeFirmwareBytes)
|
||||||
|
{
|
||||||
|
return (wholeFirmwareBytes[30] & 255) << 24
|
||||||
|
| (wholeFirmwareBytes[31] & 255) << 16
|
||||||
|
| (wholeFirmwareBytes[32] & 255) << 8
|
||||||
|
| (wholeFirmwareBytes[33] & 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getFirmware1OffsetIn(byte[] wholeFirmwareBytes)
|
||||||
|
{
|
||||||
|
return (wholeFirmwareBytes[12] & 255) << 24
|
||||||
|
| (wholeFirmwareBytes[13] & 255) << 16
|
||||||
|
| (wholeFirmwareBytes[14] & 255) << 8
|
||||||
|
| (wholeFirmwareBytes[15] & 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getFirmware1LengthIn(byte[] wholeFirmwareBytes)
|
||||||
|
{
|
||||||
|
return (wholeFirmwareBytes[16] & 255) << 24
|
||||||
|
| (wholeFirmwareBytes[17] & 255) << 16
|
||||||
|
| (wholeFirmwareBytes[18] & 255) << 8
|
||||||
|
| (wholeFirmwareBytes[19] & 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getFirmware1VersionFrom(byte[] wholeFirmwareBytes)
|
||||||
|
{
|
||||||
|
return (wholeFirmwareBytes[8] & 255) << 24
|
||||||
|
| (wholeFirmwareBytes[9] & 255) << 16
|
||||||
|
| (wholeFirmwareBytes[10] & 255) << 8
|
||||||
|
| wholeFirmwareBytes[11] & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getFirmware2VersionFrom(byte[] wholeFirmwareBytes)
|
||||||
|
{
|
||||||
|
return (wholeFirmwareBytes[22] & 255) << 24
|
||||||
|
| (wholeFirmwareBytes[23] & 255) << 16
|
||||||
|
| (wholeFirmwareBytes[24] & 255) << 8
|
||||||
|
| wholeFirmwareBytes[25] & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSingleMiBandFirmware(byte[] wholeFirmwareBytes) {
|
||||||
|
if ((wholeFirmwareBytes[7] & 255) != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;// FIXME: hack -- should be true!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -319,7 +319,6 @@ public class FetchActivityOperation extends AbstractMiBandOperation {
|
||||||
int numSamples = activityStruct.activityDataHolderProgress / bpm;
|
int numSamples = activityStruct.activityDataHolderProgress / bpm;
|
||||||
ActivitySample[] samples = new ActivitySample[numSamples];
|
ActivitySample[] samples = new ActivitySample[numSamples];
|
||||||
SampleProvider sampleProvider = new MiBandSampleProvider();
|
SampleProvider sampleProvider = new MiBandSampleProvider();
|
||||||
int s = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < activityStruct.activityDataHolderProgress; i += bpm) {
|
for (int i = 0; i < activityStruct.activityDataHolderProgress; i += bpm) {
|
||||||
category = activityStruct.activityDataHolder[i];
|
category = activityStruct.activityDataHolder[i];
|
||||||
|
|
|
@ -18,6 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
@ -27,8 +28,9 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
|
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
private boolean firmwareInfoSent = false;
|
private boolean firmwareInfoSent = false;
|
||||||
private byte[] newFirmware;
|
// private byte[] newFirmware;
|
||||||
private boolean rebootWhenBandReady = false;
|
// private boolean rebootWhenBandReady = false;
|
||||||
|
private UpdateCoordinator updateCoordinator;
|
||||||
|
|
||||||
public UpdateFirmwareOperation(Uri uri, MiBandSupport support) {
|
public UpdateFirmwareOperation(Uri uri, MiBandSupport support) {
|
||||||
super(support);
|
super(support);
|
||||||
|
@ -38,20 +40,25 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
@Override
|
@Override
|
||||||
protected void doPerform() throws IOException {
|
protected void doPerform() throws IOException {
|
||||||
MiBandFWHelper mFwHelper = new MiBandFWHelper(uri, getContext());
|
MiBandFWHelper mFwHelper = new MiBandFWHelper(uri, getContext());
|
||||||
String mMac = getDevice().getAddress();
|
|
||||||
String[] mMacOctets = mMac.split(":");
|
|
||||||
|
|
||||||
int newFwVersion = mFwHelper.getFirmwareVersion();
|
// if (getSupport().supportsHeartRate()) {
|
||||||
int oldFwVersion = getSupport().getDeviceInfo().getFirmwareVersion();
|
updateCoordinator = prepareFirmwareInfo1S(mFwHelper.getFw());
|
||||||
int checksum = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5])) ^ CheckSums.getCRC16(mFwHelper.getFw());
|
// } else {
|
||||||
|
// updateCoordinator = sendFirmwareInfo(mFwHelper.getFw(), mFwHelper.getFirmwareVersion());
|
||||||
|
// }
|
||||||
|
|
||||||
sendFirmwareInfo(oldFwVersion, newFwVersion, mFwHelper.getFw().length, checksum);
|
updateCoordinator.initNextOperation();
|
||||||
firmwareInfoSent = true;
|
updateCoordinator.initNextOperation(); // FIXME: remove, just testing mi band fw update
|
||||||
newFirmware = mFwHelper.getFw();
|
firmwareInfoSent = updateCoordinator.sendFwInfo();
|
||||||
|
if (!firmwareInfoSent) {
|
||||||
|
GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
done();
|
||||||
|
}
|
||||||
//the firmware will be sent by the notification listener if the band confirms that the metadata are ok.
|
//the firmware will be sent by the notification listener if the band confirms that the metadata are ok.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void done() {
|
private void done() {
|
||||||
|
updateCoordinator = null;
|
||||||
operationFinished();
|
operationFinished();
|
||||||
unsetBusy();
|
unsetBusy();
|
||||||
}
|
}
|
||||||
|
@ -83,32 +90,49 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
getSupport().logMessageContent(value);
|
getSupport().logMessageContent(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (updateCoordinator == null) {
|
||||||
|
LOG.error("received notification when updateCoordinator is null, ignoring!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (value[0]) {
|
switch (value[0]) {
|
||||||
case MiBandService.NOTIFY_FW_CHECK_SUCCESS:
|
case MiBandService.NOTIFY_FW_CHECK_SUCCESS:
|
||||||
if (firmwareInfoSent && newFirmware != null) {
|
// if (firmwareInfoSent && newFirmware != null) {
|
||||||
if (sendFirmwareData(newFirmware)) {
|
if (firmwareInfoSent) {
|
||||||
rebootWhenBandReady = true;
|
GB.toast(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO);
|
||||||
|
if (updateCoordinator.sendFwData()) {
|
||||||
|
// if (sendFirmwareData(newFirmware)) {
|
||||||
|
// rebootWhenBandReady = true; // disabled for testing
|
||||||
} else {
|
} else {
|
||||||
//TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do?
|
//TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do?
|
||||||
GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR);
|
GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
firmwareInfoSent = false;
|
firmwareInfoSent = false;
|
||||||
newFirmware = null;
|
// newFirmware = null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MiBandService.NOTIFY_FW_CHECK_FAILED:
|
case MiBandService.NOTIFY_FW_CHECK_FAILED:
|
||||||
GB.toast(getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR);
|
GB.toast(getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
firmwareInfoSent = false;
|
firmwareInfoSent = false;
|
||||||
newFirmware = null;
|
// newFirmware = null;
|
||||||
done();
|
done();
|
||||||
break;
|
break;
|
||||||
case MiBandService.NOTIFY_FIRMWARE_UPDATE_SUCCESS:
|
case MiBandService.NOTIFY_FIRMWARE_UPDATE_SUCCESS:
|
||||||
if (rebootWhenBandReady) {
|
if (updateCoordinator.initNextOperation()) {
|
||||||
|
GB.toast(getContext(), "Heart Rate Firmware successfully updated, now updating Mi Band Firmware", Toast.LENGTH_LONG, GB.INFO);
|
||||||
|
if (!updateCoordinator.sendFwInfo()) {
|
||||||
|
GB.toast(getContext(), "Sending Mi Band Firmware failed, aborting. Do NOT reboot your Mi Band!", Toast.LENGTH_LONG, GB.INFO);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (updateCoordinator.needsReboot()) {
|
||||||
GB.toast(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO);
|
GB.toast(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO);
|
||||||
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
|
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
|
||||||
getSupport().onReboot();
|
getSupport().onReboot();
|
||||||
rebootWhenBandReady = false;
|
// rebootWhenBandReady = false;
|
||||||
|
} else {
|
||||||
|
LOG.error("BUG: Successful firmware update without reboot???");
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
break;
|
break;
|
||||||
|
@ -116,7 +140,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
//TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do?
|
//TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do?
|
||||||
GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR);
|
GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext());
|
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext());
|
||||||
rebootWhenBandReady = false;
|
// rebootWhenBandReady = false;
|
||||||
done();
|
done();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -126,20 +150,103 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Prepare the MiBand to receive the new firmware data.
|
// * Prepare the MiBand to receive the new firmware data.
|
||||||
* Some information about the new firmware version have to be pushed to the MiBand before sending
|
// * Some information about the new firmware version have to be pushed to the MiBand before sending
|
||||||
* the actual firmare.
|
// * the actual firmare.
|
||||||
* <p/>
|
// * <p/>
|
||||||
* The Mi Band will send a notification after receiving these data to confirm if the metadata looks good to it.
|
// * The Mi Band will send a notification after receiving these data to confirm if the metadata looks good to it.
|
||||||
*
|
// *
|
||||||
* @param currentFwVersion
|
// * @param fwBytes
|
||||||
* @param newFwVersion
|
// * @param newFwVersion
|
||||||
* @param newFwSize
|
// * @see MiBandSupport#handleNotificationNotif
|
||||||
* @param checksum
|
// */
|
||||||
* @see MiBandSupport#handleNotificationNotif
|
// private byte[] sendFirmwareInfo(int currentFwVersion, int newFwVersion, int newFwSize, int checksum) throws IOException {
|
||||||
*/
|
// private UpdateCoordinator sendFirmwareInfo(byte[] fwBytes, int newFwVersion) throws IOException {
|
||||||
private void sendFirmwareInfo(int currentFwVersion, int newFwVersion, int newFwSize, int checksum) throws IOException {
|
// int newFwSize = fwBytes.length;
|
||||||
|
// String mMac = getDevice().getAddress();
|
||||||
|
// String[] mMacOctets = mMac.split(":");
|
||||||
|
// int currentFwVersion = getSupport().getDeviceInfo().getFirmwareVersion();
|
||||||
|
// int checksum = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5])) ^ CheckSums.getCRC16(fwBytes);
|
||||||
|
//
|
||||||
|
// byte[] fwInfo = new byte[]{
|
||||||
|
// MiBandService.COMMAND_SEND_FIRMWARE_INFO,
|
||||||
|
// (byte) currentFwVersion,
|
||||||
|
// (byte) (currentFwVersion >> 8),
|
||||||
|
// (byte) (currentFwVersion >> 16),
|
||||||
|
// (byte) (currentFwVersion >> 24),
|
||||||
|
// (byte) newFwVersion,
|
||||||
|
// (byte) (newFwVersion >> 8),
|
||||||
|
// (byte) (newFwVersion >> 16),
|
||||||
|
// (byte) (newFwVersion >> 24),
|
||||||
|
// (byte) newFwSize,
|
||||||
|
// (byte) (newFwSize >> 8),
|
||||||
|
// (byte) checksum,
|
||||||
|
// (byte) (checksum >> 8)
|
||||||
|
//// (byte) (checksum >> 8),
|
||||||
|
//// (byte) 0 // TEST, only for Mi1S!
|
||||||
|
// };
|
||||||
|
// return new SingleUpdateCoordinator(fwInfo, fwBytes);
|
||||||
|
// }
|
||||||
|
|
||||||
|
private UpdateCoordinator prepareFirmwareInfo1S(byte[] wholeFirmwareBytes) {
|
||||||
|
int fw2Version = Mi1SInfo.getFirmware2VersionFrom(wholeFirmwareBytes);
|
||||||
|
int fw2Offset = Mi1SInfo.getFirmware2OffsetIn(wholeFirmwareBytes);
|
||||||
|
int fw2Length = Mi1SInfo.getFirmware2LengthIn(wholeFirmwareBytes);
|
||||||
|
|
||||||
|
int fw1Version = Mi1SInfo.getFirmware1VersionFrom(wholeFirmwareBytes);
|
||||||
|
int fw1Offset = Mi1SInfo.getFirmware1OffsetIn(wholeFirmwareBytes);
|
||||||
|
int fw1Length = Mi1SInfo.getFirmware1LengthIn(wholeFirmwareBytes);
|
||||||
|
|
||||||
|
String[] mMacOctets = getDevice().getAddress().split(":");
|
||||||
|
int encodedMac = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5]));
|
||||||
|
|
||||||
|
byte[] fw2Bytes = new byte[fw2Length];
|
||||||
|
System.arraycopy(wholeFirmwareBytes, fw2Offset, fw2Bytes, 0, fw2Length);
|
||||||
|
int fw2Checksum = CheckSums.getCRC16(fw2Bytes) ^ encodedMac;
|
||||||
|
|
||||||
|
byte[] fw1Bytes = new byte[fw1Length];
|
||||||
|
System.arraycopy(wholeFirmwareBytes, fw1Offset, fw1Bytes, 0, fw1Length);
|
||||||
|
int fw1Checksum = encodedMac ^ CheckSums.getCRC16(fw1Bytes);
|
||||||
|
|
||||||
|
// check firmware validity?
|
||||||
|
|
||||||
|
int fw1OldVersion = getSupport().getDeviceInfo().getFirmwareVersion();
|
||||||
|
int fw2OldVersion = getSupport().getDeviceInfo().getHeartrateFirmwareVersion();
|
||||||
|
|
||||||
|
boolean rebootWhenFinished = true;
|
||||||
|
if (Mi1SInfo.isSingleMiBandFirmware(wholeFirmwareBytes)) {
|
||||||
|
LOG.info("is single Mi Band firmware");
|
||||||
|
byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 0, rebootWhenFinished /*, progress monitor */);
|
||||||
|
return new SingleUpdateCoordinator(fw1Info, fw1Bytes, rebootWhenFinished);
|
||||||
|
} else {
|
||||||
|
LOG.info("is multi Mi Band firmware, sending fw2 (hr) now");
|
||||||
|
byte[] fw2Info = prepareFirmwareInfo(fw2Bytes, fw2OldVersion, fw2Version, fw2Checksum, 1, rebootWhenFinished /*, progress monitor */);
|
||||||
|
byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 1, rebootWhenFinished /*, progress monitor */);
|
||||||
|
return new DoubleUpdateCoordinator(fw1Info, fw1Bytes, fw2Info, fw2Bytes, rebootWhenFinished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private Transaction createUpdateFirmwareTransaction() {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
private byte[] prepareFirmwareInfo(byte[] fwBytes, int currentFwVersion, int newFwVersion, int checksum, int something, boolean reboot) {
|
||||||
|
byte[] fwInfo;
|
||||||
|
switch (something) {
|
||||||
|
case -1:
|
||||||
|
fwInfo = prepareFirmwareUpdateA(currentFwVersion, newFwVersion, fwBytes.length, checksum);
|
||||||
|
break;
|
||||||
|
case -2:
|
||||||
|
fwInfo = prepareFirmwareUpdateB(currentFwVersion, newFwVersion, fwBytes.length, checksum, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fwInfo = prepareFirmwareUpdateB(currentFwVersion, newFwVersion, fwBytes.length, checksum, something);
|
||||||
|
}
|
||||||
|
return fwInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] prepareFirmwareUpdateA(int currentFwVersion, int newFwVersion, int newFwSize, int checksum) {
|
||||||
byte[] fwInfo = new byte[]{
|
byte[] fwInfo = new byte[]{
|
||||||
MiBandService.COMMAND_SEND_FIRMWARE_INFO,
|
MiBandService.COMMAND_SEND_FIRMWARE_INFO,
|
||||||
(byte) currentFwVersion,
|
(byte) currentFwVersion,
|
||||||
|
@ -154,15 +261,50 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
(byte) (newFwSize >> 8),
|
(byte) (newFwSize >> 8),
|
||||||
(byte) checksum,
|
(byte) checksum,
|
||||||
(byte) (checksum >> 8)
|
(byte) (checksum >> 8)
|
||||||
// (byte) (checksum >> 8),
|
|
||||||
// (byte) 0 // TEST, only for Mi1S!
|
|
||||||
};
|
};
|
||||||
TransactionBuilder builder = performInitialized("send firmware info");
|
// byte[] fwInfo = new byte[]{
|
||||||
builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext()));
|
// MiBandService.COMMAND_SEND_FIRMWARE_INFO
|
||||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), fwInfo);
|
// (byte) currentFwVersion, (byte) (currentFwVersion >> 8), (byte) (currentFwVersion >> 16), (byte) (currentFwVersion >> 24),
|
||||||
builder.queue(getQueue());
|
// (byte) newFwVersion, (byte) (newFwVersion >> 8), (byte) (newFwVersion >> 16), (byte) (newFwVersion >> 24),
|
||||||
|
// (byte) newFwSize, (byte) (newFwSize >> 8),
|
||||||
|
// (byte) checksum, (byte) (checksum >> 8)})) {
|
||||||
|
return fwInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] prepareFirmwareUpdateB(int currentFwVersion, int newFwVersion, int newFwSize, int checksum, int something) {
|
||||||
|
byte[] fwInfo = new byte[]{
|
||||||
|
MiBandService.COMMAND_SEND_FIRMWARE_INFO,
|
||||||
|
(byte) currentFwVersion,
|
||||||
|
(byte) (currentFwVersion >> 8),
|
||||||
|
(byte) (currentFwVersion >> 16),
|
||||||
|
(byte) (currentFwVersion >> 24),
|
||||||
|
(byte) newFwVersion,
|
||||||
|
(byte) (newFwVersion >> 8),
|
||||||
|
(byte) (newFwVersion >> 16),
|
||||||
|
(byte) (newFwVersion >> 24),
|
||||||
|
(byte) newFwSize,
|
||||||
|
(byte) (newFwSize >> 8),
|
||||||
|
(byte) checksum,
|
||||||
|
(byte) (checksum >> 8),
|
||||||
|
(byte) something // 0 TEST, only for Mi1S!
|
||||||
|
};
|
||||||
|
|
||||||
|
// // send to CONTROL POINT:
|
||||||
|
// if (!this.b(CONTROL_POINT, new byte[]{7,
|
||||||
|
// (byte) currentFwVersion, (byte) (currentFwVersion >> 8), (byte) (currentFwVersion >> 16), (byte) (currentFwVersion >> 24),
|
||||||
|
// (byte) newFwVersion, (byte) (newFwVersion >> 8), (byte) (newFwVersion >> 16), (byte) (newFwVersion >> 24),
|
||||||
|
// (byte) newFwSize, (byte) (newFwSize >> 8),
|
||||||
|
// (byte) checksum, (byte) (checksum >> 8), (byte) something})) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// // wait for bq != -1
|
||||||
|
// if (bq == 12) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
return fwInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that uploads a firmware (fwbytes) to the MiBand.
|
* Method that uploads a firmware (fwbytes) to the MiBand.
|
||||||
* The firmware has to be splitted into chunks of 20 bytes each, and periodically a COMMAND_SYNC comand has to be issued to the MiBand.
|
* The firmware has to be splitted into chunks of 20 bytes each, and periodically a COMMAND_SYNC comand has to be issued to the MiBand.
|
||||||
|
@ -173,18 +315,17 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
* @return whether the transfer succeeded or not. Only a BT layer exception will cause the transmission to fail.
|
* @return whether the transfer succeeded or not. Only a BT layer exception will cause the transmission to fail.
|
||||||
* @see MiBandSupport#handleNotificationNotif
|
* @see MiBandSupport#handleNotificationNotif
|
||||||
*/
|
*/
|
||||||
private boolean sendFirmwareData(byte fwbytes[]) {
|
private boolean sendFirmwareData(byte[] fwbytes) {
|
||||||
int len = fwbytes.length;
|
int len = fwbytes.length;
|
||||||
final int packetLength = 20;
|
final int packetLength = 20;
|
||||||
int packets = len / packetLength;
|
int packets = len / packetLength;
|
||||||
byte fwChunk[] = new byte[packetLength];
|
|
||||||
|
|
||||||
int firmwareProgress = 0;
|
int firmwareProgress = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("send firmware packet");
|
TransactionBuilder builder = performInitialized("send firmware packet");
|
||||||
for (int i = 0; i < packets; i++) {
|
for (int i = 0; i < packets; i++) {
|
||||||
fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength);
|
byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength);
|
||||||
|
|
||||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), fwChunk);
|
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), fwChunk);
|
||||||
firmwareProgress += packetLength;
|
firmwareProgress += packetLength;
|
||||||
|
@ -198,8 +339,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(len % packetLength == 0)) {
|
if (!(len % packetLength == 0)) {
|
||||||
byte lastChunk[] = new byte[len % packetLength];
|
byte[] lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len);
|
||||||
lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len);
|
|
||||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), lastChunk);
|
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), lastChunk);
|
||||||
firmwareProgress += len % packetLength;
|
firmwareProgress += len % packetLength;
|
||||||
}
|
}
|
||||||
|
@ -220,4 +360,134 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private abstract class UpdateCoordinator {
|
||||||
|
private final boolean reboot;
|
||||||
|
|
||||||
|
public UpdateCoordinator(boolean needsReboot) {
|
||||||
|
this.reboot = needsReboot;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract byte[] getFirmwareInfo();
|
||||||
|
|
||||||
|
public abstract byte[] getFirmwareBytes();
|
||||||
|
|
||||||
|
public abstract boolean initNextOperation();
|
||||||
|
|
||||||
|
public boolean sendFwInfo() {
|
||||||
|
try {
|
||||||
|
TransactionBuilder builder = performInitialized("send firmware info");
|
||||||
|
builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext()));
|
||||||
|
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), getFirmwareInfo());
|
||||||
|
builder.queue(getQueue());
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Error sending firmware info: " + e.getLocalizedMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sendFwData() {
|
||||||
|
return sendFirmwareData(getFirmwareBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean needsReboot() {
|
||||||
|
return false; // FIXME: renable rebooting
|
||||||
|
// return reboot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SingleUpdateCoordinator extends UpdateCoordinator {
|
||||||
|
|
||||||
|
private final byte[] fwInfo;
|
||||||
|
private final byte[] fwData;
|
||||||
|
|
||||||
|
public SingleUpdateCoordinator(byte[] fwInfo, byte[] fwData, boolean reboot) {
|
||||||
|
super(reboot);
|
||||||
|
this.fwInfo = fwInfo;
|
||||||
|
this.fwData = fwData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getFirmwareInfo() {
|
||||||
|
return fwInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getFirmwareBytes() {
|
||||||
|
return fwData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initNextOperation() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
INITIAL,
|
||||||
|
SEND_FW2,
|
||||||
|
SEND_FW1,
|
||||||
|
FINISHED, UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DoubleUpdateCoordinator extends UpdateCoordinator {
|
||||||
|
|
||||||
|
private final byte[] fw1Info;
|
||||||
|
private final byte[] fw1Data;
|
||||||
|
|
||||||
|
private final byte[] fw21nfo;
|
||||||
|
private final byte[] fw2Data;
|
||||||
|
|
||||||
|
private byte[] currentFwInfo;
|
||||||
|
private byte[] currentFwData;
|
||||||
|
|
||||||
|
private State state = State.INITIAL;
|
||||||
|
|
||||||
|
public DoubleUpdateCoordinator(byte[] fw1Info, byte[] fw1Data, byte[] fw2Info, byte[] fw2Data, boolean reboot) {
|
||||||
|
super(reboot);
|
||||||
|
this.fw1Info = fw1Info;
|
||||||
|
this.fw1Data = fw1Data;
|
||||||
|
this.fw21nfo = fw2Info;
|
||||||
|
this.fw2Data = fw2Data;
|
||||||
|
|
||||||
|
// start with fw2 (heart rate)
|
||||||
|
currentFwInfo = fw2Info;
|
||||||
|
currentFwData = fw2Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getFirmwareInfo() {
|
||||||
|
return currentFwInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getFirmwareBytes() {
|
||||||
|
return currentFwData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initNextOperation() {
|
||||||
|
switch (state) {
|
||||||
|
case INITIAL:
|
||||||
|
currentFwInfo = fw21nfo;
|
||||||
|
currentFwData = fw2Data;
|
||||||
|
state = State.SEND_FW2;
|
||||||
|
return true;
|
||||||
|
case SEND_FW2:
|
||||||
|
currentFwInfo = fw1Info;
|
||||||
|
currentFwData = fw1Data;
|
||||||
|
state = State.SEND_FW1;
|
||||||
|
return fw1Info != null && fw1Data != null;
|
||||||
|
case SEND_FW1:
|
||||||
|
currentFwInfo = null;
|
||||||
|
currentFwData = null;
|
||||||
|
state = State.FINISHED;
|
||||||
|
return false; // we're done
|
||||||
|
default:
|
||||||
|
state = State.UNKNOWN;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
<!-- Strings related to FwAppInstaller -->
|
<!-- Strings related to FwAppInstaller -->
|
||||||
<string name="title_activity_fw_app_insaller">FW/App installer</string>
|
<string name="title_activity_fw_app_insaller">FW/App installer</string>
|
||||||
<string name="fw_upgrade_notice">You are about to install firmware %s instead of the one currently on your Mi Band.</string>
|
<string name="fw_upgrade_notice">You are about to install firmware %s instead of the one currently on your Mi Band.</string>
|
||||||
|
<string name="fw_multi_upgrade_notice">You are about to install firmwares %1$s and %2$s instead of the ones currently on your Mi Band.</string>
|
||||||
<string name="miband_firmware_known">This firmware has been tested and is known to be compatible with Gadgetbridge.</string>
|
<string name="miband_firmware_known">This firmware has been tested and is known to be compatible with Gadgetbridge.</string>
|
||||||
<string name="miband_firmware_unknown_warning">"This firmware is untested and may not be compatible with Gadgetbridge.\n\nYou are NOT encouraged to flash it to your Mi Band!"</string>
|
<string name="miband_firmware_unknown_warning">"This firmware is untested and may not be compatible with Gadgetbridge.\n\nYou are NOT encouraged to flash it to your Mi Band!"</string>
|
||||||
<string name="miband_firmware_suggest_whitelist">If you still want to proceed and things continue to work properly afterwards, please tell the Gadgetbridge developers to whitelist firmware version: %s</string>
|
<string name="miband_firmware_suggest_whitelist">If you still want to proceed and things continue to work properly afterwards, please tell the Gadgetbridge developers to whitelist firmware version: %s</string>
|
||||||
|
|
Loading…
Reference in New Issue