1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-02 01:36:23 +02:00

Zepp OS: Support flashing zab files

This commit is contained in:
José Rebelo 2023-06-08 14:22:15 +01:00
parent 594a611065
commit dd01c77aca
2 changed files with 100 additions and 29 deletions

View File

@ -28,15 +28,13 @@ import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
public abstract class AbstractHuamiFirmwareInfo {
private byte[] bytes;
private final int crc16;
private final int crc32;
private int crc16;
private int crc32;
protected final HuamiFirmwareType firmwareType;
protected HuamiFirmwareType firmwareType;
public AbstractHuamiFirmwareInfo(byte[] bytes) {
this.bytes = bytes;
this.crc16 = CheckSums.getCRC16(bytes);
this.crc32 = CheckSums.getCRC32(bytes);
setBytes(bytes);
this.firmwareType = determineFirmwareType(bytes);
}
@ -70,6 +68,12 @@ public abstract class AbstractHuamiFirmwareInfo {
return crc32;
}
public void setBytes(final byte[] bytes) {
this.bytes = bytes;
this.crc16 = CheckSums.getCRC16(bytes);
this.crc32 = CheckSums.getCRC32(bytes);
}
public int getFirmwareVersion() {
return getCrc16(); // HACK until we know how to determine the version from the fw bytes
}

View File

@ -17,6 +17,8 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -114,31 +116,96 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
}
// Attempt to handle as an app / watchface
final JSONObject appJson = getAppJson(zipFile);
if (appJson == null) {
return HuamiFirmwareType.INVALID;
final JSONObject appJson = getJson(zipFile, "app.json");
if (appJson != null) {
final String appType;
try {
appType = appJson.getJSONObject("app").getString("appType");
} catch (final Exception e) {
LOG.error("Failed to get appType from app.json", e);
return HuamiFirmwareType.INVALID;
}
switch (appType) {
case "watchface":
return HuamiFirmwareType.WATCHFACE;
case "app":
return HuamiFirmwareType.APP;
default:
LOG.warn("Unknown app type {}", appType);
}
}
final String appType;
try {
appType = appJson.getJSONObject("app").getString("appType");
} catch (final Exception e) {
LOG.error("Failed to get appType from app.json", e);
return HuamiFirmwareType.INVALID;
}
switch (appType) {
case "watchface":
return HuamiFirmwareType.WATCHFACE;
case "app":
return HuamiFirmwareType.APP;
default:
LOG.warn("Unknown app type {}", appType);
// Attempt to handle as a zab file
final byte[] zpk = handleZabPackage(zipFile);
if (zpk != null) {
setBytes(zpk);
return handleZipPackage(zpk);
}
return HuamiFirmwareType.INVALID;
}
/**
* A zab package is a zip file with:
* - manifest.json
* - .sc (source code)
* - One or more zpk files
* <p>
* Right now, we only handle the first compatible zpk file that is supported by the connected device.
*/
private byte[] handleZabPackage(final ZipFile zipFile) {
final JSONObject manifest = getJson(zipFile, "manifest.json");
if (manifest == null) {
return null;
}
final JSONArray zpks;
try {
zpks = manifest.getJSONArray("zpks");
} catch (final Exception e) {
LOG.error("Failed to get zpks from manifest.json", e);
return null;
}
// Iterate all zpks until a compatible one is found
for (int i = 0; i < zpks.length(); i++) {
try {
final JSONObject zpkEntry = zpks.getJSONObject(i);
final JSONArray platforms = zpkEntry.getJSONArray("platforms");
// Check if this zpk is compatible with the current device
for (int j = 0; j < platforms.length(); j++) {
final JSONObject platform = platforms.getJSONObject(j);
if (deviceName().equals(platform.getString("name"))) {
// It's compatible with the device, fetch device.zip
final String name = zpkEntry.getString("name");
final byte[] zpkBytes = zipFile.getFileFromZip(name);
if (!ZipFile.isZipFile(zpkBytes)) {
LOG.warn("bytes for {} not a zip file", name);
continue;
}
final ZipFile zpkFile = new ZipFile(zpkBytes);
final byte[] deviceZip = zpkFile.getFileFromZip("device.zip");
if (!ZipFile.isZipFile(zpkBytes)) {
LOG.warn("bytes for device.zip of zpk {} not a zip file", name);
continue;
}
return deviceZip;
}
}
} catch (final Exception e) {
LOG.warn("Failed to parse zpk", e);
}
}
LOG.warn("No compatible zpk found in zab file");
return null;
}
@Override
public String toVersion(int crc16) {
final String crcMapVersion = getCrcMap().get(crc16);
@ -261,7 +328,7 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
// TODO check i18n section?
// TODO Show preview icon?
final JSONObject appJson = getAppJson(zipFile);
final JSONObject appJson = getJson(zipFile, "app.json");
if (appJson == null) {
return null;
}
@ -275,12 +342,12 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
return null;
}
private static JSONObject getAppJson(final ZipFile zipFile) {
private static JSONObject getJson(final ZipFile zipFile, final String path) {
final byte[] appJsonBin;
try {
appJsonBin = zipFile.getFileFromZip("app.json");
appJsonBin = zipFile.getFileFromZip(path);
} catch (final ZipFileException e) {
LOG.error("Failed to read app.json", e);
LOG.error("Failed to read " + path, e);
return null;
}
@ -290,7 +357,7 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
.replace("\uFEFF", "");
return new JSONObject(appJsonString);
} catch (final Exception e) {
LOG.error("Failed to parse app.json", e);
LOG.error("Failed to parse " + path, e);
}
return null;