2015-08-03 23:09:49 +02:00
|
|
|
package nodomain.freeyourgadget.gadgetbridge.devices.miband;
|
2015-07-23 17:14:51 +02:00
|
|
|
|
|
|
|
import android.content.ContentResolver;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.net.Uri;
|
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
import java.io.BufferedInputStream;
|
2015-08-04 23:02:36 +02:00
|
|
|
import java.io.IOException;
|
2015-07-23 17:14:51 +02:00
|
|
|
import java.io.InputStream;
|
|
|
|
import java.util.Locale;
|
|
|
|
|
2016-03-11 01:31:16 +01:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
2015-12-07 01:11:07 +01:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
2016-03-11 01:31:16 +01:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo;
|
2015-08-04 23:02:36 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
|
|
|
|
2016-03-11 01:31:16 +01:00
|
|
|
/**
|
|
|
|
* Also see Mi1SInfo.
|
|
|
|
*/
|
2015-07-23 17:14:51 +02:00
|
|
|
public class MiBandFWHelper {
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(MiBandFWHelper.class);
|
2016-02-28 02:45:35 +01:00
|
|
|
private static final int MI_FW_BASE_OFFSET = 1056;
|
|
|
|
private static final int MI1S_FW_BASE_OFFSET = 1092;
|
2015-07-23 17:14:51 +02:00
|
|
|
|
|
|
|
private final Uri uri;
|
|
|
|
private final ContentResolver cr;
|
|
|
|
private byte[] fw;
|
|
|
|
|
2016-02-28 02:45:35 +01:00
|
|
|
private int baseOffset = -1;
|
2015-07-23 17:14:51 +02:00
|
|
|
|
2015-10-22 00:53:27 +02:00
|
|
|
/**
|
|
|
|
* Provides a different notification API which is also used on Mi1A devices.
|
|
|
|
*/
|
|
|
|
public static final int FW_16779790 = 16779790;
|
|
|
|
|
2015-07-23 17:14:51 +02:00
|
|
|
private final int[] whitelistedFirmwareVersion = {
|
2015-07-28 17:30:20 +02:00
|
|
|
16779534, // 1.0.9.14 tested by developer
|
2015-07-28 22:03:53 +02:00
|
|
|
16779547, //1.0.9.27 tested by developer
|
2015-08-30 00:21:51 +02:00
|
|
|
16779568, //1.0.9.48 tested by developer
|
|
|
|
16779585, //1.0.9.65 tested by developer
|
2015-09-17 16:03:15 +02:00
|
|
|
16779779, //1.0.10.3 reported on the wiki
|
2015-12-07 01:22:27 +01:00
|
|
|
16779782, //1.0.10.6 reported on the wiki
|
2015-09-17 16:03:15 +02:00
|
|
|
16779787, //1.0.10.11 tested by developer
|
2015-10-22 00:53:27 +02:00
|
|
|
//FW_16779790, //1.0.10.14 reported on the wiki (vibration does not work currently)
|
2015-12-07 01:22:27 +01:00
|
|
|
84870926, // 5.15.7.14 tested by developer
|
2015-07-23 17:14:51 +02:00
|
|
|
};
|
|
|
|
|
2015-08-04 23:02:36 +02:00
|
|
|
public MiBandFWHelper(Uri uri, Context context) throws IOException {
|
2015-07-23 17:14:51 +02:00
|
|
|
this.uri = uri;
|
|
|
|
cr = context.getContentResolver();
|
2015-11-23 22:46:12 +01:00
|
|
|
if (cr == null) {
|
|
|
|
throw new IOException("No content resolver");
|
|
|
|
}
|
2015-07-23 17:14:51 +02:00
|
|
|
|
2016-02-28 02:45:35 +01:00
|
|
|
baseOffset = determineBaseOffset(uri);
|
2015-10-06 16:56:01 +02:00
|
|
|
String pebblePattern = ".*\\.(pbw|pbz|pbl)";
|
2015-08-31 17:40:46 +02:00
|
|
|
|
|
|
|
if (uri.getPath().matches(pebblePattern)) {
|
|
|
|
throw new IOException("Firmware has a filename that looks like a Pebble app/firmware.");
|
|
|
|
}
|
|
|
|
|
2015-09-24 14:45:21 +02:00
|
|
|
try (InputStream in = new BufferedInputStream(cr.openInputStream(uri))) {
|
2015-08-04 23:02:36 +02:00
|
|
|
this.fw = FileUtils.readAll(in, 1024 * 1024); // 1 MB
|
2016-02-28 02:45:35 +01:00
|
|
|
if (fw.length <= getOffsetFirmwareVersionMajor()) {
|
2015-12-07 01:11:07 +01:00
|
|
|
throw new IOException("This doesn't seem to be a Mi Band firmware, file size too small.");
|
2015-08-04 23:02:36 +02:00
|
|
|
}
|
2016-02-28 02:45:35 +01:00
|
|
|
byte firmwareVersionMajor = fw[getOffsetFirmwareVersionMajor()];
|
2015-12-07 01:11:07 +01:00
|
|
|
if (!isSupportedFirmwareVersionMajor(firmwareVersionMajor)) {
|
|
|
|
throw new IOException("Firmware major version not supported, either too new or this isn't a Mi Band firmware: " + firmwareVersionMajor);
|
|
|
|
}
|
|
|
|
} catch (IOException ex) {
|
|
|
|
throw ex; // pass through
|
2015-07-23 17:14:51 +02:00
|
|
|
} catch (Exception e) {
|
2015-08-04 23:02:36 +02:00
|
|
|
throw new IOException("Error reading firmware file: " + uri.toString(), e);
|
2015-07-23 17:14:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-28 02:45:35 +01:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-07 01:11:07 +01:00
|
|
|
private byte getFirmwareVersionMajor() {
|
2016-02-28 02:45:35 +01:00
|
|
|
return fw[getOffsetFirmwareVersionMajor()];
|
2015-12-07 01:11:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private byte getFirmwareVersionMinor() {
|
2016-02-28 02:45:35 +01:00
|
|
|
return fw[getOffsetFirmwareVersionMinor()];
|
2015-12-07 01:11:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isSupportedFirmwareVersionMajor(byte firmwareVersionMajor) {
|
2016-02-28 02:45:35 +01:00
|
|
|
return firmwareVersionMajor == 1 || firmwareVersionMajor == 4 || firmwareVersionMajor == 5;
|
2015-12-07 01:11:07 +01:00
|
|
|
}
|
|
|
|
|
2015-07-23 17:14:51 +02:00
|
|
|
public int getFirmwareVersion() {
|
2016-02-28 02:45:35 +01:00
|
|
|
return (fw[getOffsetFirmwareVersionMajor()] << 24) | (fw[getOffsetFirmwareVersionMinor()] << 16) | (fw[getOffsetFirmwareVersionRevision()] << 8) | fw[getOffsetFirmwareVersionBuild()];
|
2015-07-23 17:14:51 +02:00
|
|
|
}
|
|
|
|
|
2016-03-11 01:31:16 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-07-23 17:14:51 +02:00
|
|
|
public String getHumanFirmwareVersion() {
|
2016-02-28 02:45:35 +01:00
|
|
|
return String.format(Locale.US, "%d.%d.%d.%d", fw[getOffsetFirmwareVersionMajor()], fw[getOffsetFirmwareVersionMinor()], fw[getOffsetFirmwareVersionRevision()], fw[getOffsetFirmwareVersionBuild()]);
|
2015-07-23 17:14:51 +02:00
|
|
|
}
|
|
|
|
|
2016-03-11 01:31:16 +01:00
|
|
|
public String getHumanFirmwareVersion2() {
|
|
|
|
return format(Mi1SInfo.getFirmware2VersionFrom(getFw()));
|
|
|
|
}
|
|
|
|
|
|
|
|
public String format(int version) {
|
|
|
|
return formatFirmwareVersion(version);
|
|
|
|
}
|
|
|
|
|
2015-07-23 17:14:51 +02:00
|
|
|
public byte[] getFw() {
|
|
|
|
return fw;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isFirmwareWhitelisted() {
|
|
|
|
for (int wlf : whitelistedFirmwareVersion) {
|
|
|
|
if (wlf == getFirmwareVersion()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-07 01:11:07 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2016-03-16 00:22:06 +01:00
|
|
|
if (MiBandConst.MI_1S.equals(deviceHW)) {
|
2016-03-11 01:31:16 +01:00
|
|
|
return getFirmwareVersionMajor() == 4;
|
|
|
|
}
|
2015-12-07 01:11:07 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-03-11 01:31:16 +01:00
|
|
|
|
|
|
|
public boolean isSingleFirmware() {
|
|
|
|
return Mi1SInfo.isSingleMiBandFirmware(getFw());
|
|
|
|
}
|
2015-07-28 17:30:20 +02:00
|
|
|
}
|