mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-28 04:46:51 +01:00
Enhanced support for firmware detection, recognition and upgrade #234
Also supports double firmware upgrade for Mi1S. - so far, only hr firmware upgrade is tested for 1S - adds junit testcases for firmware recognition and handling
This commit is contained in:
parent
6d8d6d5bc8
commit
4f956000c5
@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -10,27 +11,23 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Locale;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.AbstractMiFirmwareInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
|
||||
/**
|
||||
* Also see Mi1SInfo.
|
||||
* Also see Mi1SFirmwareInfo.
|
||||
*/
|
||||
public class MiBandFWHelper {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MiBandFWHelper.class);
|
||||
private static final int MI_FW_BASE_OFFSET = 1056;
|
||||
private static final int MI1S_FW_BASE_OFFSET = 1092;
|
||||
|
||||
private final Uri uri;
|
||||
private final ContentResolver cr;
|
||||
private byte[] fw;
|
||||
|
||||
private int baseOffset = -1;
|
||||
private final @NonNull AbstractMiFirmwareInfo firmwareInfo;
|
||||
private final @NonNull byte[] fw;
|
||||
|
||||
/**
|
||||
* Provides a different notification API which is also used on Mi1A devices.
|
||||
@ -56,7 +53,6 @@ public class MiBandFWHelper {
|
||||
throw new IOException("No content resolver");
|
||||
}
|
||||
|
||||
baseOffset = determineBaseOffset(uri);
|
||||
String pebblePattern = ".*\\.(pbw|pbz|pbl)";
|
||||
|
||||
if (uri.getPath().matches(pebblePattern)) {
|
||||
@ -65,62 +61,23 @@ public class MiBandFWHelper {
|
||||
|
||||
try (InputStream in = new BufferedInputStream(cr.openInputStream(uri))) {
|
||||
this.fw = FileUtils.readAll(in, 1024 * 1024); // 1 MB
|
||||
if (fw.length <= getOffsetFirmwareVersionMajor()) {
|
||||
throw new IOException("This doesn't seem to be a Mi Band firmware, file size too small.");
|
||||
}
|
||||
byte firmwareVersionMajor = fw[getOffsetFirmwareVersionMajor()];
|
||||
if (!isSupportedFirmwareVersionMajor(firmwareVersionMajor)) {
|
||||
throw new IOException("Firmware major version not supported, either too new or this isn't a Mi Band firmware: " + firmwareVersionMajor);
|
||||
}
|
||||
this.firmwareInfo = determineFirmwareInfoFor(fw);
|
||||
} catch (IOException ex) {
|
||||
throw ex; // pass through
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new IOException("This doesn't seem to be a Mi Band firmware: " + ex.getLocalizedMessage(), ex);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Error reading firmware file: " + uri.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private int getOffsetFirmwareVersionMajor() {
|
||||
return baseOffset + 3;
|
||||
}
|
||||
|
||||
private int getOffsetFirmwareVersionMinor() {
|
||||
return baseOffset + 2;
|
||||
}
|
||||
|
||||
private int getOffsetFirmwareVersionRevision() {
|
||||
return baseOffset + 1;
|
||||
}
|
||||
|
||||
private int getOffsetFirmwareVersionBuild() {
|
||||
return baseOffset;
|
||||
}
|
||||
|
||||
private int determineBaseOffset(Uri uri) throws IOException {
|
||||
String name = uri.getLastPathSegment().toLowerCase();
|
||||
if (name.startsWith("mili")) {
|
||||
if (name.contains("_hr")) {
|
||||
return MI1S_FW_BASE_OFFSET;
|
||||
}
|
||||
return MI_FW_BASE_OFFSET;
|
||||
} else {
|
||||
throw new IOException("Unknown file name " + name + "; cannot recognize firmware by it.");
|
||||
}
|
||||
}
|
||||
|
||||
private byte getFirmwareVersionMajor() {
|
||||
return fw[getOffsetFirmwareVersionMajor()];
|
||||
}
|
||||
|
||||
private byte getFirmwareVersionMinor() {
|
||||
return fw[getOffsetFirmwareVersionMinor()];
|
||||
}
|
||||
|
||||
private boolean isSupportedFirmwareVersionMajor(byte firmwareVersionMajor) {
|
||||
return firmwareVersionMajor == 1 || firmwareVersionMajor == 4 || firmwareVersionMajor == 5;
|
||||
}
|
||||
|
||||
public int getFirmwareVersion() {
|
||||
return (fw[getOffsetFirmwareVersionMajor()] << 24) | (fw[getOffsetFirmwareVersionMinor()] << 16) | (fw[getOffsetFirmwareVersionRevision()] << 8) | fw[getOffsetFirmwareVersionBuild()];
|
||||
// FIXME: UnsupportedOperationException!
|
||||
return firmwareInfo.getFirst().getFirmwareVersion();
|
||||
}
|
||||
|
||||
public int getFirmware2Version() {
|
||||
return firmwareInfo.getFirst().getFirmwareVersion();
|
||||
}
|
||||
|
||||
public static String formatFirmwareVersion(int version) {
|
||||
@ -135,11 +92,11 @@ public class MiBandFWHelper {
|
||||
}
|
||||
|
||||
public String getHumanFirmwareVersion() {
|
||||
return String.format(Locale.US, "%d.%d.%d.%d", fw[getOffsetFirmwareVersionMajor()], fw[getOffsetFirmwareVersionMinor()], fw[getOffsetFirmwareVersionRevision()], fw[getOffsetFirmwareVersionBuild()]);
|
||||
return format(getFirmwareVersion());
|
||||
}
|
||||
|
||||
public String getHumanFirmwareVersion2() {
|
||||
return format(Mi1SInfo.getFirmware2VersionFrom(getFw()));
|
||||
return format(firmwareInfo.getSecond().getFirmwareVersion());
|
||||
}
|
||||
|
||||
public String format(int version) {
|
||||
@ -160,20 +117,24 @@ public class MiBandFWHelper {
|
||||
}
|
||||
|
||||
public boolean isFirmwareGenerallyCompatibleWith(GBDevice device) {
|
||||
String deviceHW = device.getHardwareVersion();
|
||||
if (MiBandConst.MI_1.equals(deviceHW)) {
|
||||
return getFirmwareVersionMajor() == 1;
|
||||
}
|
||||
if (MiBandConst.MI_1A.equals(deviceHW)) {
|
||||
return getFirmwareVersionMajor() == 5;
|
||||
}
|
||||
if (MiBandConst.MI_1S.equals(deviceHW)) {
|
||||
return getFirmwareVersionMajor() == 4;
|
||||
}
|
||||
return false;
|
||||
return firmwareInfo.isGenerallyCompatibleWith(device);
|
||||
}
|
||||
|
||||
public boolean isSingleFirmware() {
|
||||
return Mi1SInfo.isSingleMiBandFirmware(getFw());
|
||||
return firmwareInfo.isSingleMiBandFirmware();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param wholeFirmwareBytes
|
||||
* @return
|
||||
* @throws IllegalArgumentException when the data is not recognized as firmware data
|
||||
*/
|
||||
public static @NonNull AbstractMiFirmwareInfo determineFirmwareInfoFor(byte[] wholeFirmwareBytes) {
|
||||
return AbstractMiFirmwareInfo.determineFirmwareInfoFor(wholeFirmwareBytes);
|
||||
}
|
||||
|
||||
public AbstractMiFirmwareInfo getFirmwareInfo() {
|
||||
return firmwareInfo;
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ public class UserInfo {
|
||||
sequence[8] = (byte) (type & 0xff);
|
||||
|
||||
int aliasFrom = 9;
|
||||
if (mDeviceInfo.isMili1A() || mDeviceInfo.isMilli1S()) {
|
||||
if (mDeviceInfo.isMili1A() || mDeviceInfo.isMili1S()) {
|
||||
sequence[9] = (byte) (mDeviceInfo.feature & 255);
|
||||
sequence[10] = (byte) (mDeviceInfo.appearance & 255);
|
||||
aliasFrom = 11;
|
||||
|
@ -0,0 +1,74 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
/**
|
||||
* Some helper methods for Mi1 and Mi1A firmware.
|
||||
*/
|
||||
public abstract class AbstractMi1FirmwareInfo extends AbstractMiFirmwareInfo {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractMi1FirmwareInfo.class);
|
||||
|
||||
private static final int MI1_FW_BASE_OFFSET = 1056;
|
||||
|
||||
protected AbstractMi1FirmwareInfo(@NonNull byte[] wholeFirmwareBytes) {
|
||||
super(wholeFirmwareBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareOffset()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getFirmwareLength()
|
||||
{
|
||||
return wholeFirmwareBytes.length;
|
||||
}
|
||||
|
||||
public int getFirmwareVersion()
|
||||
{
|
||||
return (wholeFirmwareBytes[getOffsetFirmwareVersionMajor()] << 24)
|
||||
| (wholeFirmwareBytes[getOffsetFirmwareVersionMinor()] << 16)
|
||||
| (wholeFirmwareBytes[getOffsetFirmwareVersionRevision()] << 8)
|
||||
| wholeFirmwareBytes[getOffsetFirmwareVersionBuild()];
|
||||
}
|
||||
|
||||
private int getOffsetFirmwareVersionMajor() {
|
||||
return MI1_FW_BASE_OFFSET + 3;
|
||||
}
|
||||
|
||||
private int getOffsetFirmwareVersionMinor() {
|
||||
return MI1_FW_BASE_OFFSET + 2;
|
||||
}
|
||||
|
||||
private int getOffsetFirmwareVersionRevision() {
|
||||
return MI1_FW_BASE_OFFSET + 1;
|
||||
}
|
||||
|
||||
private int getOffsetFirmwareVersionBuild() {
|
||||
return MI1_FW_BASE_OFFSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGenerallySupportedFirmware() {
|
||||
if (!isSingleMiBandFirmware()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
int majorVersion = getFirmwareVersionMajor();
|
||||
return majorVersion == getSupportedMajorVersion();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract int getSupportedMajorVersion();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public abstract class AbstractMi1SFirmwareInfo extends AbstractMiFirmwareInfo {
|
||||
|
||||
public AbstractMi1SFirmwareInfo(@NonNull byte[] wholeFirmwareBytes) {
|
||||
super(wholeFirmwareBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerallyCompatibleWith(GBDevice device) {
|
||||
return MiBandConst.MI_1S.equals(device.getHardwareVersion());
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public abstract class AbstractMiFirmwareInfo {
|
||||
/**
|
||||
*
|
||||
* @param wholeFirmwareBytes
|
||||
* @return
|
||||
* @throws IllegalArgumentException when the data is not recognized as firmware data
|
||||
*/
|
||||
public static @NonNull AbstractMiFirmwareInfo determineFirmwareInfoFor(byte[] wholeFirmwareBytes) {
|
||||
AbstractMiFirmwareInfo[] candidates = getFirmwareInfoCandidatesFor(wholeFirmwareBytes);
|
||||
if (candidates.length == 0) {
|
||||
throw new IllegalArgumentException("Unsupported data (maybe not even a firmware?).");
|
||||
}
|
||||
if (candidates.length == 1) {
|
||||
return candidates[0];
|
||||
}
|
||||
throw new IllegalArgumentException("don't know for which device the firmware is, matches multiple devices");
|
||||
}
|
||||
|
||||
private static AbstractMiFirmwareInfo[] getFirmwareInfoCandidatesFor(byte[] wholeFirmwareBytes) {
|
||||
AbstractMiFirmwareInfo[] candidates = new AbstractMiFirmwareInfo[3];
|
||||
int i = 0;
|
||||
Mi1FirmwareInfo mi1Info = Mi1FirmwareInfo.getInstance(wholeFirmwareBytes);
|
||||
if (mi1Info != null) {
|
||||
candidates[i++] = mi1Info;
|
||||
}
|
||||
Mi1AFirmwareInfo mi1aInfo = Mi1AFirmwareInfo.getInstance(wholeFirmwareBytes);
|
||||
if (mi1aInfo != null) {
|
||||
candidates[i++] = mi1aInfo;
|
||||
}
|
||||
Mi1SFirmwareInfo mi1sInfo = Mi1SFirmwareInfo.getInstance(wholeFirmwareBytes);
|
||||
if (mi1sInfo != null) {
|
||||
candidates[i++] = mi1sInfo;
|
||||
}
|
||||
return Arrays.copyOfRange(candidates, 0, i);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected byte[] wholeFirmwareBytes;
|
||||
|
||||
public AbstractMiFirmwareInfo(@NonNull byte[] wholeFirmwareBytes) {
|
||||
this.wholeFirmwareBytes = wholeFirmwareBytes;
|
||||
}
|
||||
|
||||
public abstract int getFirmwareOffset();
|
||||
|
||||
public abstract int getFirmwareLength();
|
||||
|
||||
public abstract int getFirmwareVersion();
|
||||
|
||||
protected abstract boolean isGenerallySupportedFirmware();
|
||||
|
||||
public abstract boolean isGenerallyCompatibleWith(GBDevice device);
|
||||
|
||||
public @NonNull byte[] getFirmwareBytes() {
|
||||
return Arrays.copyOfRange(wholeFirmwareBytes, getFirmwareOffset(), getFirmwareLength());
|
||||
}
|
||||
|
||||
public int getFirmwareVersionMajor() {
|
||||
int version = getFirmwareVersion();
|
||||
if (version > 0) {
|
||||
return (version >> 24);
|
||||
}
|
||||
throw new IllegalArgumentException("bad firmware version: " + version);
|
||||
}
|
||||
|
||||
public boolean isSingleMiBandFirmware() {
|
||||
// TODO: not sure if this is a correct check!
|
||||
if ((wholeFirmwareBytes[7] & 255) != 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public AbstractMiFirmwareInfo getFirst() {
|
||||
if (isSingleMiBandFirmware()) {
|
||||
return this;
|
||||
}
|
||||
throw new UnsupportedOperationException(getClass().getName() + " must override getFirst() and getSecond()");
|
||||
}
|
||||
public AbstractMiFirmwareInfo getSecond() {
|
||||
if (isSingleMiBandFirmware()) {
|
||||
throw new UnsupportedOperationException(getClass().getName() + " only supports on firmware");
|
||||
}
|
||||
throw new UnsupportedOperationException(getClass().getName() + " must override getFirst() and getSecond()");
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||
|
||||
public class DeviceInfo extends AbstractInfo {
|
||||
@ -76,7 +75,7 @@ public class DeviceInfo extends AbstractInfo {
|
||||
}
|
||||
|
||||
public boolean supportsHeartrate() {
|
||||
return isMilli1S();
|
||||
return isMili1S();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,7 +99,7 @@ public class DeviceInfo extends AbstractInfo {
|
||||
return feature == 5 && appearance == 0 || feature == 0 && hwVersion == 208;
|
||||
}
|
||||
|
||||
public boolean isMilli1S() {
|
||||
public boolean isMili1S() {
|
||||
// TODO: this is probably not quite correct, but hopefully sufficient for early 1S support
|
||||
return feature == 4 && appearance == 0 || feature == 4 && hwVersion == 4;
|
||||
}
|
||||
@ -112,7 +111,7 @@ public class DeviceInfo extends AbstractInfo {
|
||||
if (isMili1A()) {
|
||||
return MiBandConst.MI_1A;
|
||||
}
|
||||
if (isMilli1S()) {
|
||||
if (isMili1S()) {
|
||||
return MiBandConst.MI_1S;
|
||||
}
|
||||
return "?";
|
||||
|
@ -0,0 +1,37 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public class Mi1AFirmwareInfo extends AbstractMi1FirmwareInfo {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Mi1AFirmwareInfo.class);
|
||||
|
||||
public static Mi1AFirmwareInfo getInstance(byte[] wholeFirmwareBytes) {
|
||||
Mi1AFirmwareInfo info = new Mi1AFirmwareInfo(wholeFirmwareBytes);
|
||||
if (info.isGenerallySupportedFirmware()) {
|
||||
return info;
|
||||
}
|
||||
LOG.info("firmware not supported");
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Mi1AFirmwareInfo(@NonNull byte[] wholeFirmwareBytes) {
|
||||
super(wholeFirmwareBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSupportedMajorVersion() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerallyCompatibleWith(GBDevice device) {
|
||||
String hwVersion = device.getHardwareVersion();
|
||||
return MiBandConst.MI_1A.equals(hwVersion);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public class Mi1FirmwareInfo extends AbstractMi1FirmwareInfo {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Mi1FirmwareInfo.class);
|
||||
|
||||
public static Mi1FirmwareInfo getInstance(byte[] wholeFirmwareBytes) {
|
||||
Mi1FirmwareInfo info = new Mi1FirmwareInfo(wholeFirmwareBytes);
|
||||
if (info.isGenerallySupportedFirmware()) {
|
||||
return info;
|
||||
}
|
||||
LOG.info("firmware not supported");
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Mi1FirmwareInfo(@NonNull byte[] wholeFirmwareBytes) {
|
||||
super(wholeFirmwareBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSupportedMajorVersion() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerallyCompatibleWith(GBDevice device) {
|
||||
String hwVersion = device.getHardwareVersion();
|
||||
return MiBandConst.MI_1.equals(hwVersion);
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* FW1 is Mi Band firmware
|
||||
* FW2 is heartrate firmware
|
||||
*/
|
||||
public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractMi1FirmwareInfo.class);
|
||||
|
||||
private final Mi1SFirmwareInfoFW1 fw1Info;
|
||||
private final Mi1SFirmwareInfoFW2 fw2Info;
|
||||
|
||||
private Mi1SFirmwareInfo(byte[] wholeFirmwareBytes) {
|
||||
super(wholeFirmwareBytes);
|
||||
fw1Info = new Mi1SFirmwareInfoFW1(wholeFirmwareBytes);
|
||||
fw2Info = new Mi1SFirmwareInfoFW2(wholeFirmwareBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractMiFirmwareInfo getFirst() {
|
||||
return fw1Info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractMiFirmwareInfo getSecond() {
|
||||
return fw2Info;
|
||||
}
|
||||
|
||||
public static @Nullable Mi1SFirmwareInfo getInstance(byte[] wholeFirmwareBytes) {
|
||||
Mi1SFirmwareInfo info = new Mi1SFirmwareInfo(wholeFirmwareBytes);
|
||||
if (info.isGenerallySupportedFirmware()) {
|
||||
return info;
|
||||
}
|
||||
LOG.info("firmware not supported");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGenerallySupportedFirmware() {
|
||||
if (isSingleMiBandFirmware()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return fw1Info.isGenerallySupportedFirmware() && fw2Info.isGenerallySupportedFirmware();
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareOffset() {
|
||||
throw new UnsupportedOperationException("call this method on getFirmwareXInfo()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareLength() {
|
||||
throw new UnsupportedOperationException("call this method on getFirmwareXInfo()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareVersion() {
|
||||
throw new UnsupportedOperationException("call this method on getFirmwareXInfo()");
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* FW1 is Mi Band firmware
|
||||
* FW2 is heartrate firmware
|
||||
*/
|
||||
public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo {
|
||||
|
||||
private static final int MI1S_FW_BASE_OFFSET = 1092;
|
||||
|
||||
Mi1SFirmwareInfoFW1(@NonNull byte[] wholeFirmwareBytes) {
|
||||
super(wholeFirmwareBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareOffset()
|
||||
{
|
||||
return (wholeFirmwareBytes[12] & 255) << 24
|
||||
| (wholeFirmwareBytes[13] & 255) << 16
|
||||
| (wholeFirmwareBytes[14] & 255) << 8
|
||||
| (wholeFirmwareBytes[15] & 255);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareLength()
|
||||
{
|
||||
return (wholeFirmwareBytes[16] & 255) << 24
|
||||
| (wholeFirmwareBytes[17] & 255) << 16
|
||||
| (wholeFirmwareBytes[18] & 255) << 8
|
||||
| (wholeFirmwareBytes[19] & 255);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareVersion()
|
||||
{
|
||||
return (wholeFirmwareBytes[8] & 255) << 24
|
||||
| (wholeFirmwareBytes[9] & 255) << 16
|
||||
| (wholeFirmwareBytes[10] & 255) << 8
|
||||
| wholeFirmwareBytes[11] & 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGenerallySupportedFirmware() {
|
||||
try {
|
||||
int majorVersion = getFirmwareVersionMajor();
|
||||
return majorVersion == 4;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* FW1 is Mi Band firmware
|
||||
* FW2 is heartrate firmware
|
||||
*/
|
||||
public class Mi1SFirmwareInfoFW2 extends AbstractMi1SFirmwareInfo {
|
||||
|
||||
Mi1SFirmwareInfoFW2(@NonNull byte[] wholeFirmwareBytes) {
|
||||
super(wholeFirmwareBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareOffset()
|
||||
{
|
||||
return (wholeFirmwareBytes[26] & 255) << 24
|
||||
| (wholeFirmwareBytes[27] & 255) << 16
|
||||
| (wholeFirmwareBytes[28] & 255) << 8
|
||||
| (wholeFirmwareBytes[29] & 255);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareLength()
|
||||
{
|
||||
return (wholeFirmwareBytes[30] & 255) << 24
|
||||
| (wholeFirmwareBytes[31] & 255) << 16
|
||||
| (wholeFirmwareBytes[32] & 255) << 8
|
||||
| (wholeFirmwareBytes[33] & 255);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGenerallySupportedFirmware() {
|
||||
try {
|
||||
int majorVersion = getFirmwareVersionMajor();
|
||||
return majorVersion == 1;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareVersion()
|
||||
{
|
||||
return (wholeFirmwareBytes[22] & 255) << 24
|
||||
| (wholeFirmwareBytes[23] & 255) << 16
|
||||
| (wholeFirmwareBytes[24] & 255) << 8
|
||||
| wholeFirmwareBytes[25] & 255;
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* FW1 is Mi Band firmware
|
||||
* FW2 is heartrate firmware
|
||||
*/
|
||||
public class Mi1SInfo {
|
||||
|
||||
public static int getFirmware2OffsetIn(@NonNull byte[] wholeFirmwareBytes)
|
||||
{
|
||||
return (wholeFirmwareBytes[26] & 255) << 24
|
||||
| (wholeFirmwareBytes[27] & 255) << 16
|
||||
| (wholeFirmwareBytes[28] & 255) << 8
|
||||
| (wholeFirmwareBytes[29] & 255);
|
||||
}
|
||||
|
||||
public static int getFirmware2LengthIn(@NonNull byte[] wholeFirmwareBytes)
|
||||
{
|
||||
return (wholeFirmwareBytes[30] & 255) << 24
|
||||
| (wholeFirmwareBytes[31] & 255) << 16
|
||||
| (wholeFirmwareBytes[32] & 255) << 8
|
||||
| (wholeFirmwareBytes[33] & 255);
|
||||
}
|
||||
|
||||
public static int getFirmware1OffsetIn(@NonNull byte[] wholeFirmwareBytes)
|
||||
{
|
||||
return (wholeFirmwareBytes[12] & 255) << 24
|
||||
| (wholeFirmwareBytes[13] & 255) << 16
|
||||
| (wholeFirmwareBytes[14] & 255) << 8
|
||||
| (wholeFirmwareBytes[15] & 255);
|
||||
}
|
||||
|
||||
public static int getFirmware1LengthIn(@NonNull byte[] wholeFirmwareBytes)
|
||||
{
|
||||
return (wholeFirmwareBytes[16] & 255) << 24
|
||||
| (wholeFirmwareBytes[17] & 255) << 16
|
||||
| (wholeFirmwareBytes[18] & 255) << 8
|
||||
| (wholeFirmwareBytes[19] & 255);
|
||||
}
|
||||
|
||||
public static int getFirmware1VersionFrom(@NonNull byte[] wholeFirmwareBytes)
|
||||
{
|
||||
return (wholeFirmwareBytes[8] & 255) << 24
|
||||
| (wholeFirmwareBytes[9] & 255) << 16
|
||||
| (wholeFirmwareBytes[10] & 255) << 8
|
||||
| wholeFirmwareBytes[11] & 255;
|
||||
}
|
||||
|
||||
public static int getFirmware2VersionFrom(@NonNull byte[] wholeFirmwareBytes)
|
||||
{
|
||||
return (wholeFirmwareBytes[22] & 255) << 24
|
||||
| (wholeFirmwareBytes[23] & 255) << 16
|
||||
| (wholeFirmwareBytes[24] & 255) << 8
|
||||
| wholeFirmwareBytes[25] & 255;
|
||||
}
|
||||
|
||||
// FIXME: this method is wrong. We don't know a way to check if a firmware file
|
||||
// contains one or more firmwares.
|
||||
public static boolean isSingleMiBandFirmware(@NonNull byte[] wholeFirmwareBytes) {
|
||||
if ((wholeFirmwareBytes[7] & 255) != 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -18,7 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.AbstractMiFirmwareInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
@ -28,8 +28,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
|
||||
private final Uri uri;
|
||||
private boolean firmwareInfoSent = false;
|
||||
// private byte[] newFirmware;
|
||||
// private boolean rebootWhenBandReady = false;
|
||||
private UpdateCoordinator updateCoordinator;
|
||||
|
||||
public UpdateFirmwareOperation(Uri uri, MiBandSupport support) {
|
||||
@ -41,11 +39,16 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
protected void doPerform() throws IOException {
|
||||
MiBandFWHelper mFwHelper = new MiBandFWHelper(uri, getContext());
|
||||
|
||||
// if (getSupport().supportsHeartRate()) {
|
||||
updateCoordinator = prepareFirmwareInfo1S(mFwHelper.getFw());
|
||||
// } else {
|
||||
// updateCoordinator = sendFirmwareInfo(mFwHelper.getFw(), mFwHelper.getFirmwareVersion());
|
||||
// }
|
||||
AbstractMiFirmwareInfo firmwareInfo = mFwHelper.getFirmwareInfo();
|
||||
if (!firmwareInfo.isGenerallyCompatibleWith(getDevice())) {
|
||||
throw new IOException("Firmware is not compatible with the given device: " + getDevice().getAddress());
|
||||
}
|
||||
|
||||
if (getSupport().supportsHeartRate()) {
|
||||
updateCoordinator = prepareFirmwareInfo1S(firmwareInfo);
|
||||
} else {
|
||||
updateCoordinator = prepareFirmwareInfo(mFwHelper.getFw(), mFwHelper.getFirmwareVersion());
|
||||
}
|
||||
|
||||
updateCoordinator.initNextOperation();
|
||||
// updateCoordinator.initNextOperation(); // FIXME: remove, just testing mi band 1s fw update
|
||||
@ -75,7 +78,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
}
|
||||
|
||||
/**
|
||||
* React to unsolicited messages sent by the Mi Band to the MiBandService.UUID_CHARACTERISTIC_NOTIFICATION
|
||||
* React to messages sent by the Mi Band to the MiBandService.UUID_CHARACTERISTIC_NOTIFICATION
|
||||
* characteristic,
|
||||
* These messages appear to be always 1 byte long, with values that are listed in MiBandService.
|
||||
* It is not excluded that there are further values which are still unknown.
|
||||
@ -97,25 +100,19 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
|
||||
switch (value[0]) {
|
||||
case MiBandService.NOTIFY_FW_CHECK_SUCCESS:
|
||||
// if (firmwareInfoSent && newFirmware != null) {
|
||||
if (firmwareInfoSent) {
|
||||
GB.toast(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO);
|
||||
if (updateCoordinator.sendFwData()) {
|
||||
// if (sendFirmwareData(newFirmware)) {
|
||||
// rebootWhenBandReady = true; // disabled for testing
|
||||
} else {
|
||||
if (!updateCoordinator.sendFwData()) {
|
||||
//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);
|
||||
done();
|
||||
}
|
||||
firmwareInfoSent = false;
|
||||
// newFirmware = null;
|
||||
}
|
||||
break;
|
||||
case MiBandService.NOTIFY_FW_CHECK_FAILED:
|
||||
GB.toast(getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR);
|
||||
firmwareInfoSent = false;
|
||||
// newFirmware = null;
|
||||
done();
|
||||
break;
|
||||
case MiBandService.NOTIFY_FIRMWARE_UPDATE_SUCCESS:
|
||||
@ -130,7 +127,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
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());
|
||||
getSupport().onReboot();
|
||||
// rebootWhenBandReady = false;
|
||||
} else {
|
||||
LOG.error("BUG: Successful firmware update without reboot???");
|
||||
}
|
||||
@ -140,7 +136,6 @@ 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?
|
||||
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());
|
||||
// rebootWhenBandReady = false;
|
||||
done();
|
||||
break;
|
||||
|
||||
@ -150,63 +145,41 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 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
|
||||
// * the actual firmare.
|
||||
// * <p/>
|
||||
// * The Mi Band will send a notification after receiving these data to confirm if the metadata looks good to it.
|
||||
// *
|
||||
// * @param fwBytes
|
||||
// * @param newFwVersion
|
||||
// * @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 {
|
||||
// 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);
|
||||
// }
|
||||
/**
|
||||
* 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
|
||||
* the actual firmare.
|
||||
* <p/>
|
||||
* The Mi Band will send a notification after receiving these data to confirm if the metadata looks good to it.
|
||||
*
|
||||
* @param newFwVersion
|
||||
* @see MiBandSupport#handleNotificationNotif
|
||||
*/
|
||||
private UpdateCoordinator prepareFirmwareInfo(byte[] fwBytes, int newFwVersion) 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);
|
||||
|
||||
private UpdateCoordinator prepareFirmwareInfo1S(byte[] wholeFirmwareBytes) {
|
||||
int fw2Version = Mi1SInfo.getFirmware2VersionFrom(wholeFirmwareBytes);
|
||||
int fw2Offset = Mi1SInfo.getFirmware2OffsetIn(wholeFirmwareBytes);
|
||||
int fw2Length = Mi1SInfo.getFirmware2LengthIn(wholeFirmwareBytes);
|
||||
byte[] fwInfo = prepareFirmwareUpdateA(currentFwVersion, newFwVersion, newFwSize, checksum);
|
||||
return new SingleUpdateCoordinator(fwInfo, fwBytes, true);
|
||||
}
|
||||
|
||||
int fw1Version = Mi1SInfo.getFirmware1VersionFrom(wholeFirmwareBytes);
|
||||
int fw1Offset = Mi1SInfo.getFirmware1OffsetIn(wholeFirmwareBytes);
|
||||
int fw1Length = Mi1SInfo.getFirmware1LengthIn(wholeFirmwareBytes);
|
||||
private UpdateCoordinator prepareFirmwareInfo1S(AbstractMiFirmwareInfo info) {
|
||||
if (info.isSingleMiBandFirmware()) {
|
||||
throw new IllegalArgumentException("preparing single fw not allowed for 1S");
|
||||
}
|
||||
int fw2Version = info.getSecond().getFirmwareVersion();
|
||||
int fw1Version = info.getFirst().getFirmwareVersion();
|
||||
|
||||
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);
|
||||
byte[] fw2Bytes = info.getSecond().getFirmwareBytes();
|
||||
int fw2Checksum = CheckSums.getCRC16(fw2Bytes) ^ encodedMac;
|
||||
|
||||
byte[] fw1Bytes = new byte[fw1Length];
|
||||
System.arraycopy(wholeFirmwareBytes, fw1Offset, fw1Bytes, 0, fw1Length);
|
||||
byte[] fw1Bytes = info.getFirst().getFirmwareBytes();
|
||||
int fw1Checksum = encodedMac ^ CheckSums.getCRC16(fw1Bytes);
|
||||
|
||||
// check firmware validity?
|
||||
@ -215,22 +188,18 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
int fw2OldVersion = getSupport().getDeviceInfo().getHeartrateFirmwareVersion();
|
||||
|
||||
boolean rebootWhenFinished = true;
|
||||
if (Mi1SInfo.isSingleMiBandFirmware(wholeFirmwareBytes)) {
|
||||
if (info.isSingleMiBandFirmware()) {
|
||||
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");
|
||||
LOG.info("is multi Mi Band firmware, sending fw2 (hr) first");
|
||||
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) {
|
||||
@ -262,12 +231,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
(byte) checksum,
|
||||
(byte) (checksum >> 8)
|
||||
};
|
||||
// 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)})) {
|
||||
return fwInfo;
|
||||
}
|
||||
|
||||
@ -286,21 +249,8 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
(byte) (newFwSize >> 8),
|
||||
(byte) checksum,
|
||||
(byte) (checksum >> 8),
|
||||
(byte) something // 0 TEST, only for Mi1S!
|
||||
(byte) something
|
||||
};
|
||||
|
||||
// // 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;
|
||||
}
|
||||
|
||||
@ -388,6 +338,9 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation {
|
||||
}
|
||||
|
||||
public boolean sendFwData() {
|
||||
// if (true) {
|
||||
// return true; // FIXME: temporarily disabled firmware sending
|
||||
// }
|
||||
return sendFirmwareData(getFirmwareBytes());
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import org.junit.Test;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
@ -18,19 +19,19 @@ public class FirmwareTest {
|
||||
private static final int MI1S_FW1_VERSION = 0;
|
||||
private static final int MI1S_FW2_VERSION = 0;
|
||||
|
||||
private static final int SINGLE = 1;
|
||||
private static final int DOUBLE = 2;
|
||||
|
||||
@Test
|
||||
public void testFirmwareMi() throws Exception{
|
||||
public void testFirmwareMi1() throws Exception{
|
||||
byte[] wholeFw = getFirmwareMi();
|
||||
Assert.assertNotNull(wholeFw);
|
||||
|
||||
Assert.assertTrue(Mi1SInfo.isSingleMiBandFirmware(wholeFw));
|
||||
int calculatedLength = Mi1SInfo.getFirmware1LengthIn(wholeFw);
|
||||
Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength < wholeFw.length);
|
||||
int calculatedVersion = Mi1SInfo.getFirmware1VersionFrom(wholeFw);
|
||||
// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI_FW_VERSION, calculatedVersion);
|
||||
|
||||
AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, SINGLE);
|
||||
int calculatedVersion = info.getFirmwareVersion();
|
||||
String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion);
|
||||
Assert.assertTrue(version.startsWith("1."));
|
||||
// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI_FW_VERSION, calculatedVersion);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -38,14 +39,11 @@ public class FirmwareTest {
|
||||
byte[] wholeFw = getFirmwareMi1A();
|
||||
Assert.assertNotNull(wholeFw);
|
||||
|
||||
Assert.assertTrue(Mi1SInfo.isSingleMiBandFirmware(wholeFw));
|
||||
int calculatedLength = Mi1SInfo.getFirmware1LengthIn(wholeFw);
|
||||
Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength < wholeFw.length);
|
||||
int calculatedVersion = Mi1SInfo.getFirmware1VersionFrom(wholeFw);
|
||||
// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI1A_FW_VERSION, calculatedVersion);
|
||||
|
||||
AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, SINGLE);
|
||||
int calculatedVersion = info.getFirmwareVersion();
|
||||
String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion);
|
||||
Assert.assertTrue(version.startsWith("5."));
|
||||
// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI1A_FW_VERSION, calculatedVersion);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -53,31 +51,71 @@ public class FirmwareTest {
|
||||
byte[] wholeFw = getFirmwareMi1S();
|
||||
Assert.assertNotNull(wholeFw);
|
||||
|
||||
Assert.assertFalse(Mi1SInfo.isSingleMiBandFirmware(wholeFw));
|
||||
AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, DOUBLE);
|
||||
|
||||
// Mi Band version
|
||||
int calculatedLengthFw1 = Mi1SInfo.getFirmware1LengthIn(wholeFw);
|
||||
int calculatedOffsetFw1 = Mi1SInfo.getFirmware1OffsetIn(wholeFw);
|
||||
int calculatedLengthFw1 = info.getFirst().getFirmwareLength();
|
||||
int calculatedOffsetFw1 = info.getFirst().getFirmwareOffset();
|
||||
int endIndexFw1 = calculatedOffsetFw1 + calculatedLengthFw1;
|
||||
|
||||
int calculatedLengthFw2 = Mi1SInfo.getFirmware2LengthIn(wholeFw);
|
||||
int calculatedOffsetFw2 = Mi1SInfo.getFirmware2OffsetIn(wholeFw);
|
||||
int calculatedLengthFw2 = info.getSecond().getFirmwareLength();
|
||||
int calculatedOffsetFw2 = info.getSecond().getFirmwareOffset();
|
||||
int endIndexFw2 = calculatedOffsetFw2 + calculatedLengthFw2;
|
||||
|
||||
Assert.assertTrue(endIndexFw1 <= wholeFw.length - calculatedLengthFw2);
|
||||
Assert.assertTrue(endIndexFw2 <= wholeFw.length);
|
||||
|
||||
Assert.assertTrue(endIndexFw1 <= calculatedOffsetFw2);
|
||||
int calculatedVersionFw1 = Mi1SInfo.getFirmware1VersionFrom(wholeFw);
|
||||
int calculatedVersionFw1 = info.getFirst().getFirmwareVersion();
|
||||
// Assert.assertEquals("Unexpected firmware 1 version: " + calculatedVersionFw1, MI1S_FW1_VERSION, calculatedVersionFw1);
|
||||
String version1 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw1);
|
||||
Assert.assertTrue(version1.startsWith("4."));
|
||||
|
||||
// HR version
|
||||
int calculatedVersionFw2 = Mi1SInfo.getFirmware2VersionFrom(wholeFw);
|
||||
int calculatedVersionFw2 = info.getSecond().getFirmwareVersion();
|
||||
// Assert.assertEquals("Unexpected firmware 2 version: " + calculatedVersionFw2, MI1S_FW2_VERSION, calculatedVersionFw2);
|
||||
String version2 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw2);
|
||||
Assert.assertTrue(version2.startsWith("1."));
|
||||
|
||||
try {
|
||||
info.getFirmwareVersion();
|
||||
Assert.fail("should not get fw version from AbstractMi1SFirmwareInfo");
|
||||
} catch (UnsupportedOperationException expected) {}
|
||||
|
||||
Assert.assertNotEquals(info.getFirst().getFirmwareOffset(), info.getSecond().getFirmwareOffset());
|
||||
Assert.assertFalse(Arrays.equals(info.getFirst().getFirmwareBytes(), info.getSecond().getFirmwareBytes()));
|
||||
}
|
||||
|
||||
private AbstractMiFirmwareInfo getFirmwareInfo(byte[] wholeFw, int numFirmwares) {
|
||||
AbstractMiFirmwareInfo info = AbstractMiFirmwareInfo.determineFirmwareInfoFor(wholeFw);
|
||||
switch (numFirmwares) {
|
||||
case SINGLE: {
|
||||
Assert.assertTrue("should be single miband firmware", info.isSingleMiBandFirmware());
|
||||
Assert.assertSame(info, info.getFirst());
|
||||
try {
|
||||
info.getSecond();
|
||||
Assert.fail("should throw UnsuportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {}
|
||||
int calculatedLength = info.getFirmwareLength();
|
||||
Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength <= wholeFw.length);
|
||||
break;
|
||||
}
|
||||
case DOUBLE: {
|
||||
Assert.assertFalse("should not be single miband firmware", info.isSingleMiBandFirmware());
|
||||
Assert.assertNotSame(info, info.getFirst());
|
||||
Assert.assertNotSame(info, info.getSecond());
|
||||
Assert.assertNotSame(info.getFirst(), info.getSecond());
|
||||
int calculatedLength = info.getFirst().getFirmwareLength();
|
||||
Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength <= wholeFw.length);
|
||||
calculatedLength = info.getSecond().getFirmwareLength();
|
||||
Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength <= wholeFw.length);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Assert.fail("unexpected numFirmwares: " + numFirmwares);
|
||||
}
|
||||
Assert.assertTrue(info.isGenerallySupportedFirmware());
|
||||
return info;
|
||||
}
|
||||
|
||||
private File getFirmwareDir() {
|
||||
|
Loading…
Reference in New Issue
Block a user