mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-03-13 09:40:15 +01:00
Huawei: initial firmware update support
This commit is contained in:
parent
0f2d240402
commit
0e3d6dffd2
@ -88,6 +88,9 @@ public class HuaweiCoordinator {
|
||||
|
||||
private int maxContactsCount = 0;
|
||||
|
||||
private String otaSoftwareVersion = null;
|
||||
private int otaSignatureLength = 256;
|
||||
|
||||
public HuaweiCoordinator(HuaweiCoordinatorSupplier parent) {
|
||||
this.parent = parent;
|
||||
|
||||
@ -619,6 +622,23 @@ public class HuaweiCoordinator {
|
||||
return supportsCommandForService(0x34, 0x1);
|
||||
}
|
||||
|
||||
public boolean supportsOTAUpdate() {
|
||||
return supportsCommandForService(0x09, 0x01);
|
||||
}
|
||||
|
||||
public boolean supportsOTAAutoUpdate() {
|
||||
return supportsCommandForService(0x09, 0x0c);
|
||||
}
|
||||
|
||||
public boolean supportsOTANotify() {
|
||||
return supportsCommandForService(0x09, 0x0e);
|
||||
}
|
||||
|
||||
public boolean supportsOTADeviceRequest() {
|
||||
return supportsCommandForService(0x09, 0x0f);
|
||||
}
|
||||
|
||||
|
||||
public boolean supportsExternalCalendarService() {
|
||||
if (supportsExpandCapability())
|
||||
return supportsExpandCapability(184);
|
||||
@ -696,14 +716,13 @@ public class HuaweiCoordinator {
|
||||
return supportsExpandCapability(82);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean supportsDeviceCommandConfig() {
|
||||
if (supportsExpandCapability())
|
||||
return supportsExpandCapability(83);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean supportsDeviceCommandEvent() {
|
||||
if (supportsExpandCapability())
|
||||
return supportsExpandCapability(84);
|
||||
@ -733,7 +752,24 @@ public class HuaweiCoordinator {
|
||||
return supportsExpandCapability(156);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsOTAChangelog() {
|
||||
if (supportsExpandCapability())
|
||||
return supportsExpandCapability(52);
|
||||
return false;
|
||||
}
|
||||
public boolean supportsOTASignature() {
|
||||
if (supportsExpandCapability())
|
||||
return supportsExpandCapability(144);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsWiFiDirect() {
|
||||
if (supportsExpandCapability())
|
||||
return supportsExpandCapability(147);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsPromptPushMessage () {
|
||||
// do not ask for capabilities under specific condition
|
||||
// if (deviceType == 10 && deviceVersion == 73617766697368 && deviceSoftVersion == 372E312E31) -> leo device
|
||||
@ -924,4 +960,20 @@ public class HuaweiCoordinator {
|
||||
public void setSupportsGpsNewSync(boolean supportsGpsNewSync) {
|
||||
this.supportsGpsNewSync = supportsGpsNewSync;
|
||||
}
|
||||
|
||||
public String getOtaSoftwareVersion() {
|
||||
return otaSoftwareVersion;
|
||||
}
|
||||
|
||||
public void setOtaSoftwareVersion(String otaSoftwareVersion) {
|
||||
this.otaSoftwareVersion = otaSoftwareVersion;
|
||||
}
|
||||
|
||||
public int getOtaSignatureLength() {
|
||||
return otaSignatureLength;
|
||||
}
|
||||
|
||||
public void setOtaSignatureLength(int otaSignatureLength) {
|
||||
this.otaSignatureLength = otaSignatureLength;
|
||||
}
|
||||
}
|
||||
|
@ -116,6 +116,46 @@ public class HuaweiInstallHandler implements InstallHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if(helper.isFirmware) {
|
||||
this.valid = true; //NOTE: nothing to verify for now
|
||||
|
||||
installActivity.setInstallEnabled(true);
|
||||
|
||||
GenericItem installItem = new GenericItem();
|
||||
|
||||
installItem.setName(helper.fwInfo.versionName);
|
||||
installActivity.setInstallItem(installItem);
|
||||
if (device.isBusy()) {
|
||||
LOG.error("Firmware cannot be uploaded (device busy)");
|
||||
installActivity.setInfoText("Firmware cannot be uploaded (device busy)");
|
||||
installActivity.setInfoText(device.getBusyTask());
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!device.isConnected()) {
|
||||
LOG.error("Firmware cannot be uploaded(not connected or wrong device)");
|
||||
installActivity.setInfoText("Firmware cannot be uploaded (not connected or wrong device)");
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.valid) {
|
||||
LOG.error("Firmware cannot be uploaded");
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
//installItem.setDetails(config.version);
|
||||
|
||||
installItem.setIcon(R.drawable.ic_firmware);
|
||||
|
||||
//installActivity.setInfoText(context.getString(R.string.app_install_info, installItem.getName(), config.version, config.vendor));
|
||||
|
||||
LOG.debug("Initialized HuaweiInstallHandler: Firmware");
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper.isWatchface()) {
|
||||
final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator;
|
||||
|
||||
|
@ -41,6 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.EphemerisFile
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService0A;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService2C;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather;
|
||||
@ -740,6 +741,36 @@ public class HuaweiPacket {
|
||||
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
|
||||
return this;
|
||||
}
|
||||
case OTA.id:
|
||||
switch (this.commandId) {
|
||||
case OTA.StartQuery.id:
|
||||
return new OTA.StartQuery.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.DataParams.id:
|
||||
return new OTA.DataParams.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.DataChunkRequest.id:
|
||||
return new OTA.DataChunkRequest.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.SizeReport.id:
|
||||
return new OTA.SizeReport.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.UpdateResult.id:
|
||||
return new OTA.UpdateResult.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.DeviceError.id:
|
||||
return new OTA.DeviceError.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.SetAutoUpdate.id:
|
||||
return new OTA.SetAutoUpdate.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.NotifyNewVersion.id:
|
||||
return new OTA.NotifyNewVersion.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.DeviceRequest.id:
|
||||
return new OTA.DeviceRequest.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.GetMode.id:
|
||||
return new OTA.GetMode.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.SetChangeLog.id:
|
||||
return new OTA.SetChangeLog.Response(paramsProvider).fromPacket(this);
|
||||
case OTA.GetChangeLog.id:
|
||||
return new OTA.GetChangeLog.Response(paramsProvider).fromPacket(this);
|
||||
default:
|
||||
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
|
||||
return this;
|
||||
}
|
||||
default:
|
||||
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
|
||||
return this;
|
||||
@ -865,6 +896,11 @@ public class HuaweiPacket {
|
||||
return retv;
|
||||
}
|
||||
|
||||
protected List<byte[]> serializeOTAGetMode() {
|
||||
byte[] serializedTLV = { 0x01, 0x01};
|
||||
return isSliced?serializeSliced(serializedTLV):serializeUnsliced(serializedTLV);
|
||||
}
|
||||
|
||||
public List<byte[]> serializeFileChunk(byte[] fileChunk, int uploadPosition, int unitSize, byte fileId, boolean isEncrypted) throws SerializeException {
|
||||
List<byte[]> retv = new ArrayList<>();
|
||||
final int subHeaderLength = 6;
|
||||
@ -948,6 +984,53 @@ public class HuaweiPacket {
|
||||
return retv;
|
||||
}
|
||||
|
||||
public List<byte[]> serializeOTAChunk(byte[] fileChunk, int offset, int unitSize, boolean addOffset, List<Integer> bitmap) throws SerializeException {
|
||||
List<byte[]> retv = new ArrayList<>();
|
||||
|
||||
int maxUnitSize = unitSize - 9;
|
||||
int packetCount = (int) Math.ceil(((double) fileChunk.length) / (double) maxUnitSize);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(fileChunk);
|
||||
int sliceStart = offset;
|
||||
int chunkIdx = 0;
|
||||
for (int i = 0; i < packetCount; i++) {
|
||||
|
||||
if (chunkIdx > 0xff) {
|
||||
chunkIdx = 0;
|
||||
}
|
||||
|
||||
int contentSize = Math.min(maxUnitSize, buffer.remaining());
|
||||
|
||||
if ((bitmap != null) && (bitmap.size() > i)) {
|
||||
// NOTE: skip already delivered parts
|
||||
if (bitmap.get(i) != 0) {
|
||||
byte[] packetContent = new byte[contentSize];
|
||||
buffer.get(packetContent);
|
||||
sliceStart += maxUnitSize;
|
||||
chunkIdx++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ByteBuffer payload;
|
||||
if (addOffset) {
|
||||
payload = ByteBuffer.allocate(contentSize + 4 + 1);
|
||||
payload.putInt(sliceStart);
|
||||
} else {
|
||||
payload = ByteBuffer.allocate(contentSize + 1);
|
||||
}
|
||||
payload.put((byte)chunkIdx);
|
||||
byte[] packetContent = new byte[contentSize];
|
||||
buffer.get(packetContent);
|
||||
payload.put(packetContent);
|
||||
retv.addAll(serializeSliced(payload.array()));
|
||||
|
||||
sliceStart += maxUnitSize;
|
||||
chunkIdx++;
|
||||
|
||||
}
|
||||
return retv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<byte[]> serialize() throws CryptoException {
|
||||
|
@ -289,6 +289,18 @@ public class HuaweiTLV {
|
||||
return ByteBuffer.wrap(getBytes(tag)).getInt();
|
||||
}
|
||||
|
||||
public Long getAsLong(int tag) throws HuaweiPacket.MissingTagException {
|
||||
byte[] bytes = getBytes(tag);
|
||||
if(bytes.length == 1) {
|
||||
return (long) (bytes[0] & 0xFF);
|
||||
} else if(bytes.length == 2) {
|
||||
return (long) (ByteBuffer.wrap(getBytes(tag)).getShort() & 0xFFFF);
|
||||
} else if(bytes.length == 4) {
|
||||
return (long) (ByteBuffer.wrap(getBytes(tag)).getInt());
|
||||
}
|
||||
return ByteBuffer.wrap(getBytes(tag)).getLong();
|
||||
}
|
||||
|
||||
public String getString(int tag) throws HuaweiPacket.MissingTagException {
|
||||
return new String(getBytes(tag), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huawei.ota;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
public class HuaweiOTAFileList {
|
||||
|
||||
static public class OTAComponent {
|
||||
public String name;
|
||||
public int compress;
|
||||
}
|
||||
|
||||
static public class OTAFileInfo {
|
||||
public String spath;
|
||||
public String dpath;
|
||||
public String operation;
|
||||
public String md5;
|
||||
public String sha256;
|
||||
public long size;
|
||||
public String packageName;
|
||||
public String versionName;
|
||||
public int versionCode;
|
||||
}
|
||||
|
||||
public OTAComponent component = new OTAComponent();
|
||||
public List<OTAFileInfo> files = new ArrayList<>();
|
||||
|
||||
|
||||
public static HuaweiOTAFileList getFileList(String xmlStr) {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
try {
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(new InputSource(new StringReader(
|
||||
xmlStr)));
|
||||
|
||||
|
||||
HuaweiOTAFileList ret = new HuaweiOTAFileList();
|
||||
|
||||
NodeList component = doc.getElementsByTagName("component");
|
||||
if (component.item(0).getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element elem = (Element) component.item(0);
|
||||
ret.component.name = elem.getElementsByTagName("name").item(0).getTextContent();
|
||||
ret.component.compress = Integer.parseInt(elem.getElementsByTagName("compress").item(0).getTextContent());
|
||||
}
|
||||
|
||||
NodeList files = doc.getElementsByTagName("file");
|
||||
for (int i = 0; i < files.getLength(); i++) {
|
||||
Node fnode = files.item(i);
|
||||
|
||||
if (fnode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
|
||||
Element elem = (Element) fnode;
|
||||
OTAFileInfo info = new OTAFileInfo();
|
||||
|
||||
info.spath = elem.getElementsByTagName("spath").item(0).getTextContent();
|
||||
info.dpath = elem.getElementsByTagName("dpath").item(0).getTextContent();
|
||||
info.operation = elem.getElementsByTagName("operation").item(0).getTextContent();
|
||||
info.md5 = elem.getElementsByTagName("md5").item(0).getTextContent();
|
||||
info.sha256 = elem.getElementsByTagName("sha256").item(0).getTextContent();
|
||||
info.size = Long.parseLong(elem.getElementsByTagName("size").item(0).getTextContent());
|
||||
info.packageName = elem.getElementsByTagName("packageName").item(0).getTextContent();
|
||||
info.versionName = elem.getElementsByTagName("versionName").item(0).getTextContent();
|
||||
info.versionCode = Integer.parseInt(elem.getElementsByTagName("versionCode").item(0).getTextContent());
|
||||
|
||||
ret.files.add(info);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -453,6 +453,7 @@ public class DeviceConfig {
|
||||
public String hardwareVersion;
|
||||
public String softwareVersion;
|
||||
public String productModel;
|
||||
public int otaSignatureLength = 256;
|
||||
|
||||
public Response(ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
@ -467,6 +468,9 @@ public class DeviceConfig {
|
||||
this.hardwareVersion = this.tlv.getString(0x03);
|
||||
this.softwareVersion = this.tlv.getString(0x07);
|
||||
this.productModel = this.tlv.getString(0x0A).trim();
|
||||
|
||||
if(this.tlv.contains(0x27))
|
||||
this.otaSignatureLength = this.tlv.getAsInteger(0x27);
|
||||
}
|
||||
}
|
||||
|
||||
@ -811,7 +815,7 @@ public class DeviceConfig {
|
||||
case 0x13: // Force buildOsEnable to 0x00
|
||||
case 0x14: // Force buildOSApiVersion to 0x00
|
||||
default:
|
||||
this.tlv.put(b, (byte)00);
|
||||
this.tlv.put(b, (byte)0);
|
||||
}
|
||||
}
|
||||
this.complete = true;
|
||||
|
@ -0,0 +1,445 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiOTAManager;
|
||||
|
||||
public class OTA {
|
||||
public static final byte id = 0x09;
|
||||
|
||||
public static class StartQuery {
|
||||
public static final byte id = 0x01;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider, String firmwareVersion, short fileId, byte operation, boolean add) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01, firmwareVersion)
|
||||
.put(0x02, fileId)
|
||||
.put(0x03, operation);
|
||||
if(add)
|
||||
this.tlv.put(0x05, (byte)5);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public byte batteryThreshold;
|
||||
public int respCode;
|
||||
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x4)) {
|
||||
batteryThreshold = this.tlv.getByte(0x4);
|
||||
}
|
||||
if(this.tlv.contains(0x7f)) {
|
||||
respCode = this.tlv.getInteger(0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataParams {
|
||||
public static final byte id = 0x02;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public HuaweiOTAManager.UploadInfo info = new HuaweiOTAManager.UploadInfo();
|
||||
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x1))
|
||||
info.waitTimeout = this.tlv.getAsInteger(0x1);
|
||||
if(this.tlv.contains(0x2))
|
||||
info.restartTimeout = this.tlv.getAsInteger(0x2);
|
||||
|
||||
info.maxUnitSize = this.tlv.getAsInteger(0x3);
|
||||
|
||||
if(this.tlv.contains(0x4))
|
||||
info.interval = this.tlv.getAsLong(0x4);
|
||||
if(this.tlv.contains(0x5))
|
||||
info.ack = (this.tlv.getAsLong(0x5) == 1);
|
||||
if(this.tlv.contains(0x6))
|
||||
info.offset = (this.tlv.getAsLong(0x6) == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataChunkRequest {
|
||||
public static final byte id = 0x03;
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public Integer errorCode = null;
|
||||
public long fileOffset;
|
||||
public long chunkLength;
|
||||
public byte[] bitmap = null;
|
||||
public long wifiSend = 0;
|
||||
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x7f)) {
|
||||
errorCode = this.tlv.getInteger(0x7f);
|
||||
return;
|
||||
}
|
||||
|
||||
fileOffset = this.tlv.getAsLong(0x1);
|
||||
chunkLength = this.tlv.getAsLong(0x2);
|
||||
if(this.tlv.contains(0x3))
|
||||
bitmap = this.tlv.getBytes(0x3);
|
||||
if(this.tlv.contains(0x4))
|
||||
wifiSend = this.tlv.getAsLong(0x4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NextChunkSend extends HuaweiPacket {
|
||||
public static final byte id = 0x04;
|
||||
|
||||
public NextChunkSend(ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SizeReport {
|
||||
public static final byte id = 0x05;
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
|
||||
public long size = 0;
|
||||
public long current = 0;
|
||||
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x1))
|
||||
size = this.tlv.getAsLong(0x1);
|
||||
if(this.tlv.contains(0x2))
|
||||
current = this.tlv.getAsLong(0x2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class UpdateResult {
|
||||
public static final byte id = 0x06;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public int resultCode = 0; // 0 fail, 1 -success
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x01)) {
|
||||
resultCode = this.tlv.getAsInteger(0x01);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeviceError {
|
||||
public static final byte id = 0x07;
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public int errorCode;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x7f)) {
|
||||
errorCode = this.tlv.getInteger(0x7f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetStatus {
|
||||
public static final byte id = 0x09;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider, Byte useWifi) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV().put(0x01, (byte)0x01);
|
||||
if(useWifi != null) {
|
||||
this.tlv.put(0x02, useWifi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class SetAutoUpdate {
|
||||
public static final byte id = 0x0c;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider, boolean state) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV().put(0x01, state);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public int respCode;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x7f)) {
|
||||
respCode = this.tlv.getInteger(0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class NotifyNewVersion {
|
||||
public static final byte id = 0x0e;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider, String newSoftwareVersion, long fileSize, byte unkn1, byte unkn2) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01, newSoftwareVersion)
|
||||
.put(0x02, fileSize)
|
||||
.put(0x03, System.currentTimeMillis() / 1000)
|
||||
.put(0x04, unkn1) // NOTE: 1, 2 or 3. If not 1 do not add 0x01 and 0x02 tlv
|
||||
.put(0x05, unkn2); // NOTE: 2 or 0. I don't understand meaning of this byte
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public int respCode;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x7f)) {
|
||||
respCode = this.tlv.getInteger(0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeviceRequest {
|
||||
public static final byte id = 0x0f;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider, int code) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x7f, code);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public byte unkn1;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x01)) {
|
||||
unkn1 = this.tlv.getByte(0x01);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Progress {
|
||||
public static final byte id = 0x12;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider, byte percent, byte state, byte mode) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01, percent)
|
||||
.put(0x02, state)
|
||||
.put(0x03, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetMode {
|
||||
public static final byte id = 0x13;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<byte[]> serialize() throws CryptoException {
|
||||
return super.serializeOTAGetMode();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public byte mode = (byte) 255;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x01)) {
|
||||
mode = this.tlv.getByte(0x01);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetChangeLog {
|
||||
public static final byte id = 0x14;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
// TODO: discover how to proper encode changelog
|
||||
// result = 0 - no changelog data
|
||||
public Request(ParamsProvider paramsProvider, String version, byte result) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01, version)
|
||||
.put(0x02, result);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public int respCode;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x7f)) {
|
||||
respCode = this.tlv.getInteger(0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetChangeLog {
|
||||
public static final byte id = 0x15;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
|
||||
public Request(ParamsProvider paramsProvider, String version, String language) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = id;
|
||||
this.isEncrypted = false; // NOTE: not sure but looks like unencrypted
|
||||
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01, version)
|
||||
.put(0x02, language);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
byte result;
|
||||
String uri;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x03))
|
||||
result = this.tlv.getByte(0x03);
|
||||
if(this.tlv.contains(0x04))
|
||||
uri = this.tlv.getString(0x04);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -212,8 +212,8 @@ public class Watchface {
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
public static byte reportType = 0;
|
||||
public static String fileName;
|
||||
public byte reportType = 0;
|
||||
public String fileName;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Menstrual;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Notifications;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather;
|
||||
@ -133,6 +134,8 @@ public class AsynchronousResponse {
|
||||
handleNotifications(response);
|
||||
handlePermissionCheck(response);
|
||||
handleDataSyncCommands(response);
|
||||
handleOTA(response);
|
||||
|
||||
} catch (Request.ResponseParseException e) {
|
||||
LOG.error("Response parse exception", e);
|
||||
}
|
||||
@ -415,7 +418,7 @@ public class AsynchronousResponse {
|
||||
try {
|
||||
getPhoneInfoReq.doPerform();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
LOG.error("Error send GetPhoneInfoRequest", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -431,7 +434,7 @@ public class AsynchronousResponse {
|
||||
try {
|
||||
sendMenstrualModifyTimeReq.doPerform();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
LOG.error("Error send SendMenstrualModifyTimeRequest", e);
|
||||
}
|
||||
|
||||
}
|
||||
@ -797,4 +800,35 @@ public class AsynchronousResponse {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOTA(HuaweiPacket response) throws Request.ResponseTypeMismatchException {
|
||||
if (response.serviceId != OTA.id)
|
||||
return;
|
||||
if (response.commandId == OTA.DeviceRequest.id) {
|
||||
if (!(response instanceof OTA.DeviceRequest.Response)) {
|
||||
throw new Request.ResponseTypeMismatchException(response, OTA.DeviceRequest.class);
|
||||
}
|
||||
OTA.DeviceRequest.Response resp = (OTA.DeviceRequest.Response) response;
|
||||
support.getHuaweiOTAManager().handleDeviceRequest(resp.unkn1);
|
||||
} else if (response.commandId == OTA.DataChunkRequest.id) {
|
||||
if (!(response instanceof OTA.DataChunkRequest.Response)) {
|
||||
throw new Request.ResponseTypeMismatchException(response, OTA.DataChunkRequest.class);
|
||||
}
|
||||
support.getHuaweiOTAManager().handleDataChunkRequest((OTA.DataChunkRequest.Response) response);
|
||||
} else if (response.commandId == OTA.SizeReport.id) {
|
||||
if (!(response instanceof OTA.SizeReport.Response)) {
|
||||
throw new Request.ResponseTypeMismatchException(response, OTA.SizeReport.class);
|
||||
}
|
||||
support.getHuaweiOTAManager().handleSizeReport(((OTA.SizeReport.Response) response).size, ((OTA.SizeReport.Response) response).current);
|
||||
} else if (response.commandId == OTA.UpdateResult.id) {
|
||||
if (!(response instanceof OTA.UpdateResult.Response)) {
|
||||
throw new Request.ResponseTypeMismatchException(response, OTA.UpdateResult.class);
|
||||
}
|
||||
support.getHuaweiOTAManager().handleUploadResult(((OTA.UpdateResult.Response) response).resultCode);
|
||||
} else if (response.commandId == OTA.DeviceError.id) {
|
||||
if (!(response instanceof OTA.DeviceError.Response)) {
|
||||
throw new Request.ResponseTypeMismatchException(response, OTA.DeviceError.class);
|
||||
}
|
||||
support.getHuaweiOTAManager().handleDeviceError(((OTA.DeviceError.Response) response).errorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaExtractor;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.media.MediaFormat;
|
||||
import android.media.MediaFormat;
|
||||
import android.net.Uri;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.TextUtils;
|
||||
@ -37,8 +37,11 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiBinAppParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.ota.HuaweiOTAFileList;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBZipFile;
|
||||
@ -55,12 +58,15 @@ public class HuaweiFwHelper {
|
||||
private byte fileType = 0;
|
||||
String fileName = "";
|
||||
|
||||
|
||||
Bitmap previewBitmap;
|
||||
HuaweiWatchfaceManager.WatchfaceDescription watchfaceDescription;
|
||||
HuaweiAppManager.AppConfig appConfig;
|
||||
HuaweiMusicManager.AudioInfo musicInfo;
|
||||
Context mContext;
|
||||
|
||||
public boolean isFirmware = false;
|
||||
public HuaweiOTAFileList.OTAFileInfo fwInfo = null;
|
||||
|
||||
|
||||
public HuaweiFwHelper(final Uri uri, final Context context) {
|
||||
@ -79,7 +85,9 @@ public class HuaweiFwHelper {
|
||||
}
|
||||
|
||||
private void parseFile() {
|
||||
if (parseAsMusic()) {
|
||||
if (parseAsFirmware()) {
|
||||
isFirmware = true;
|
||||
} else if (parseAsMusic()) {
|
||||
fileType = FileUpload.Filetype.music;
|
||||
} else if (parseAsApp()) {
|
||||
assert appConfig.bundleName != null;
|
||||
@ -91,8 +99,67 @@ public class HuaweiFwHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean parseAsFirmware() {
|
||||
try {
|
||||
final UriHelper uriHelper = UriHelper.get(uri, this.mContext);
|
||||
|
||||
ZipInputStream stream = new ZipInputStream(uriHelper.openInputStream());
|
||||
byte[] fileListXml = null;
|
||||
ZipEntry entry;
|
||||
while ((entry = stream.getNextEntry()) != null) {
|
||||
if (entry.getName().equals("filelist.xml")) {
|
||||
fileListXml = GBZipFile.readAllBytes(stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream.close();
|
||||
|
||||
if (fileListXml == null) {
|
||||
LOG.info("Firmware: filelist.xml not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
HuaweiOTAFileList fileList = HuaweiOTAFileList.getFileList(new String(fileListXml));
|
||||
if (fileList == null) {
|
||||
LOG.error("Firmware: filelist.xml is invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG.info("Firmware: {}", fileList.component.name);
|
||||
|
||||
for (HuaweiOTAFileList.OTAFileInfo info : fileList.files) {
|
||||
LOG.info("Firmware: file {}", info.dpath);
|
||||
if (info.dpath.endsWith(".bin.apk")) {
|
||||
fwInfo = info;
|
||||
}
|
||||
}
|
||||
|
||||
if (fwInfo == null) {
|
||||
LOG.error("Firmware: required files not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean valid = false;
|
||||
stream = new ZipInputStream(uriHelper.openInputStream());
|
||||
while ((entry = stream.getNextEntry()) != null) {
|
||||
if (entry.getName().equals(fwInfo.dpath)) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream.close();
|
||||
|
||||
LOG.info("Firmware: valid: {}", valid);
|
||||
return valid;
|
||||
} catch (Exception e) {
|
||||
LOG.error("Firmware: error occurred", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getNameWithoutExtension(String fileName) {
|
||||
return fileName.indexOf(".") > 0?fileName.substring(0, fileName.lastIndexOf(".")): fileName;
|
||||
return fileName.indexOf(".") > 0 ? fileName.substring(0, fileName.lastIndexOf(".")) : fileName;
|
||||
}
|
||||
|
||||
private String getExtension(String fileName) {
|
||||
@ -111,7 +178,7 @@ public class HuaweiFwHelper {
|
||||
String[] filePathColumn = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
|
||||
|
||||
Cursor cursor = contentResolver.query(selectedUri, filePathColumn, null, null, null);
|
||||
if(cursor == null)
|
||||
if (cursor == null)
|
||||
return null;
|
||||
cursor.moveToFirst();
|
||||
|
||||
@ -127,10 +194,10 @@ public class HuaweiFwHelper {
|
||||
String title = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
|
||||
String artist = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
|
||||
|
||||
if(TextUtils.isEmpty(title)) {
|
||||
if (TextUtils.isEmpty(title)) {
|
||||
title = getNameWithoutExtension(fileName);
|
||||
}
|
||||
if(TextUtils.isEmpty(artist)) {
|
||||
if (TextUtils.isEmpty(artist)) {
|
||||
artist = "Unknown";
|
||||
}
|
||||
|
||||
@ -145,13 +212,13 @@ public class HuaweiFwHelper {
|
||||
int channels = mf.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
|
||||
long duration = mf.getLong(MediaFormat.KEY_DURATION);
|
||||
|
||||
LOG.info("bitRate: " + bitrate);
|
||||
LOG.info("sampleRate: " + sampleRate);
|
||||
LOG.info("channelCount: " + channels);
|
||||
LOG.info("duration: " + duration);
|
||||
LOG.info("bitRate: {}", bitrate);
|
||||
LOG.info("sampleRate: {}", sampleRate);
|
||||
LOG.info("channelCount: {}", channels);
|
||||
LOG.info("duration: {}", duration);
|
||||
|
||||
String extension = getExtension(fileName);
|
||||
if(!TextUtils.isEmpty(extension)) {
|
||||
if (!TextUtils.isEmpty(extension)) {
|
||||
extension = extension.toLowerCase();
|
||||
}
|
||||
|
||||
@ -182,11 +249,11 @@ public class HuaweiFwHelper {
|
||||
String mimeType = mContext.getContentResolver().getType(uri);
|
||||
LOG.info("File mime type: {}", mimeType);
|
||||
|
||||
if(mimeType == null || !mimeType.startsWith("audio/"))
|
||||
if (mimeType == null || !mimeType.startsWith("audio/"))
|
||||
return false;
|
||||
|
||||
musicInfo = getAudioInfo(uri);
|
||||
if(musicInfo == null)
|
||||
if (musicInfo == null)
|
||||
return false;
|
||||
|
||||
musicInfo.setMimeType(mimeType);
|
||||
@ -323,7 +390,7 @@ public class HuaweiFwHelper {
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return isWatchface() || isAPP() || isMusic();
|
||||
return isWatchface() || isAPP() || isMusic() || isFirmware;
|
||||
}
|
||||
|
||||
public Bitmap getPreviewBitmap() {
|
||||
|
@ -0,0 +1,605 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.ota.HuaweiOTAFileList;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTADataChunkRequestAck;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTADataParamsRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTADeviceRequestReply;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTAFileChunk;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTAGetMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTANotifyNewVersion;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTAProgress;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTASetStatus;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTAStartQuery;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTAUploadResultAck;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
|
||||
|
||||
public class HuaweiOTAManager {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HuaweiOTAManager.class);
|
||||
|
||||
public static class UploadInfo {
|
||||
public int waitTimeout;
|
||||
public int restartTimeout;
|
||||
public int maxUnitSize;
|
||||
public long interval = 0;
|
||||
public boolean ack = false;
|
||||
public boolean offset = false;
|
||||
}
|
||||
|
||||
static class FirmwareCheckHandler extends Handler {
|
||||
private final WeakReference<HuaweiOTAManager> otaManager;
|
||||
|
||||
FirmwareCheckHandler(HuaweiOTAManager otaManager) {
|
||||
super();
|
||||
this.otaManager = new WeakReference<>(otaManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(@NonNull Message msg) {
|
||||
HuaweiOTAManager manager = otaManager.get();
|
||||
if (manager == null)
|
||||
return;
|
||||
switch (msg.what) {
|
||||
case 1:
|
||||
Integer val = (Integer) msg.obj;
|
||||
manager.onFwCheckProgress(val);
|
||||
break;
|
||||
case 2:
|
||||
File fwFile = (File) msg.obj;
|
||||
manager.onFwCheckSuccess(fwFile);
|
||||
break;
|
||||
case 3:
|
||||
manager.onFwCheckFail();
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unknown what in message: {}", msg.what);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final HuaweiSupportProvider support;
|
||||
|
||||
|
||||
private final FirmwareCheckHandler firmwareCheckHandler = new FirmwareCheckHandler(this);
|
||||
|
||||
private int downloadProgress = 0;
|
||||
|
||||
|
||||
private int state = 0;
|
||||
|
||||
private HuaweiOTAFileList.OTAFileInfo fwInfo;
|
||||
private Uri uri;
|
||||
|
||||
private File fwFile = null;
|
||||
private FileInputStream fwInputStream = null;
|
||||
|
||||
private UploadInfo currentUploadInfo = null;
|
||||
|
||||
public HuaweiOTAManager(HuaweiSupportProvider support) {
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
if (fwInputStream != null) {
|
||||
try {
|
||||
fwInputStream.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error close input stream", e);
|
||||
}
|
||||
fwInputStream = null;
|
||||
}
|
||||
if (this.fwFile != null) {
|
||||
this.fwFile.delete();
|
||||
this.fwFile = null;
|
||||
}
|
||||
this.fwInfo = null;
|
||||
this.uri = null;
|
||||
currentUploadInfo = null;
|
||||
this.state = 0;
|
||||
this.downloadProgress = 0;
|
||||
unsetDeviceBusy();
|
||||
}
|
||||
|
||||
public void startFwUpdate(HuaweiOTAFileList.OTAFileInfo fwInfo, Uri uri) {
|
||||
this.fwInfo = fwInfo;
|
||||
this.uri = uri;
|
||||
this.state = 1;
|
||||
if (fwInfo == null || uri == null) {
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
if (!support.getHuaweiCoordinator().supportsOTAUpdate()) {
|
||||
LOG.info("OTA update is not supported");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
setDeviceBusy();
|
||||
|
||||
if (support.getHuaweiCoordinator().supportsOTANotify()) {
|
||||
try {
|
||||
SendOTANotifyNewVersion notifyNewVersion = new SendOTANotifyNewVersion(support, this.fwInfo.versionName, this.fwInfo.size, (byte) 1, (byte) 2);
|
||||
notifyNewVersion.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTANotifyNewVersion", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
SendOTAStartQuery sendOTAStartQuery = new SendOTAStartQuery(support, this.fwInfo.versionName, (short) 256, (byte) 2, false);
|
||||
sendOTAStartQuery.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAStartQuery", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void startFWCheck(HuaweiOTAFileList.OTAFileInfo fwInfo, Uri uri) {
|
||||
downloadProgress = 0;
|
||||
if (support.getHuaweiCoordinator().supportsOTADeviceRequest()) {
|
||||
try {
|
||||
SendOTAProgress progressReq = new SendOTAProgress(support, (byte) 0, (byte) 0, (byte) 0);
|
||||
progressReq.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAProgress", e);
|
||||
}
|
||||
}
|
||||
new Thread(new Runnable() {
|
||||
|
||||
private void sendMessage(int what, Object obj) {
|
||||
Message msg = Message.obtain();
|
||||
msg.obj = obj;
|
||||
msg.what = what;
|
||||
firmwareCheckHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
File fwFile = null;
|
||||
int lastProgress = 0;
|
||||
try {
|
||||
sendMessage(1, lastProgress);
|
||||
|
||||
final UriHelper uriHelper = UriHelper.get(uri, support.getContext());
|
||||
File outputDir = support.getContext().getCacheDir();
|
||||
fwFile = new File(outputDir, fwInfo.dpath);
|
||||
|
||||
FileOutputStream outputStream = new FileOutputStream(fwFile);
|
||||
ZipInputStream stream = new ZipInputStream(uriHelper.openInputStream());
|
||||
ZipEntry entry;
|
||||
while ((entry = stream.getNextEntry()) != null) {
|
||||
if (entry.getName().equals(fwInfo.dpath)) {
|
||||
int n;
|
||||
byte[] buf = new byte[1024];
|
||||
int cnt = 0;
|
||||
while ((n = stream.read(buf, 0, buf.length)) != -1) {
|
||||
if (n > 0) {
|
||||
outputStream.write(buf, 0, n);
|
||||
cnt += n;
|
||||
int progress = (int) ((((double) cnt / fwInfo.size) / 2.0d) * 100.0d);
|
||||
if (progress > lastProgress) {
|
||||
lastProgress = progress;
|
||||
sendMessage(1, lastProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream.close();
|
||||
|
||||
LOG.info("Firmware size: {} -- {}", fwFile.length(), fwInfo.size);
|
||||
|
||||
if (fwFile.length() != fwInfo.size) {
|
||||
throw new Exception("Size is not correct");
|
||||
}
|
||||
|
||||
sendMessage(1, 49);
|
||||
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
MessageDigest sha256 = MessageDigest.getInstance("SHA256");
|
||||
|
||||
FileInputStream inStream = new FileInputStream(fwFile);
|
||||
int n;
|
||||
int cnt = 0;
|
||||
byte[] buf = new byte[1024];
|
||||
while ((n = inStream.read(buf)) != -1) {
|
||||
if (n > 0) {
|
||||
md5.update(buf, 0, n);
|
||||
sha256.update(buf, 0, n);
|
||||
cnt += n;
|
||||
int progress = 49 + (int) ((((double) cnt / fwInfo.size) / 2.0d) * 100.0d);
|
||||
if (progress > lastProgress) {
|
||||
lastProgress = progress;
|
||||
sendMessage(1, lastProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
inStream.close();
|
||||
|
||||
byte[] md5hash = md5.digest();
|
||||
byte[] sha256hash = sha256.digest();
|
||||
LOG.info("md5: {} -- {}", GB.hexdump(md5hash), fwInfo.md5);
|
||||
LOG.info("sha256: {} -- {}", GB.hexdump(sha256hash), fwInfo.sha256);
|
||||
if (!GB.hexdump(md5hash).equals(fwInfo.md5) || !GB.hexdump(sha256hash).equals(fwInfo.sha256)) {
|
||||
throw new Exception("Hash mismatch");
|
||||
}
|
||||
|
||||
sendMessage(1, 100);
|
||||
sendMessage(2, fwFile);
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("Check firmware: Error occurred", e);
|
||||
if (fwFile != null) {
|
||||
fwFile.delete();
|
||||
}
|
||||
sendMessage(3, null);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private short calculateFwFileId() throws Exception {
|
||||
FileInputStream inStream = new FileInputStream(this.fwFile);
|
||||
byte[] buf = new byte[8];
|
||||
int n = inStream.read(buf);
|
||||
inStream.close();
|
||||
if (n < 0) {
|
||||
throw new Exception("invalid data");
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(buf, 4, 4);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
int data = buffer.getInt();
|
||||
|
||||
int signature = 256;
|
||||
if (support.getHuaweiCoordinator().supportsOTASignature()) {
|
||||
signature = support.getHuaweiCoordinator().getOtaSignatureLength();
|
||||
}
|
||||
LOG.info("data: {}, signature: {}", data, signature);
|
||||
return (short) (data + signature);
|
||||
}
|
||||
|
||||
private byte[] getFileChunk(long offset, long len) {
|
||||
if (this.fwFile == null || !this.fwFile.exists()) {
|
||||
LOG.error("no file");
|
||||
return null;
|
||||
}
|
||||
if (this.fwInputStream == null) {
|
||||
try {
|
||||
fwInputStream = new FileInputStream(this.fwFile);
|
||||
} catch (IOException e) {
|
||||
LOG.error("error open file", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ByteBuffer buf = ByteBuffer.allocate((int) len);
|
||||
int n = fwInputStream.getChannel().read(buf, offset);
|
||||
if (n >= 0) {
|
||||
return Arrays.copyOfRange(buf.array(), 0, n);
|
||||
}
|
||||
LOG.error("File read error");
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
LOG.error("Read file exception", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void startFWUpload() {
|
||||
short fileId;
|
||||
try {
|
||||
fileId = calculateFwFileId();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error getting ID");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
setUploadProgress(0, true);
|
||||
|
||||
try {
|
||||
SendOTAStartQuery sendOTAStartQuery = new SendOTAStartQuery(support, fwInfo.versionName, fileId, (byte) 0, true);
|
||||
sendOTAStartQuery.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAStartQuery", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onFwCheckFail() {
|
||||
LOG.info("onFwCheckFail");
|
||||
if (support.getHuaweiCoordinator().supportsOTADeviceRequest()) {
|
||||
try {
|
||||
SendOTAProgress progressReq = new SendOTAProgress(support, (byte) downloadProgress, (byte) 2, (byte) 0);
|
||||
progressReq.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAProgress", e);
|
||||
}
|
||||
}
|
||||
setCheckingFailed();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void onFwCheckSuccess(File fwFile) {
|
||||
LOG.info("onFwCheckSuccess: {}", fwFile.getAbsoluteFile());
|
||||
|
||||
setCheckingProgress(100, false);
|
||||
|
||||
this.fwFile = fwFile;
|
||||
|
||||
this.state = 3;
|
||||
|
||||
if (support.getHuaweiCoordinator().supportsOTADeviceRequest()) {
|
||||
try {
|
||||
SendOTAGetMode sendOTAGetMode = new SendOTAGetMode(support);
|
||||
sendOTAGetMode.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAGetMode", e);
|
||||
}
|
||||
} else {
|
||||
// start upload
|
||||
startFWUpload();
|
||||
}
|
||||
}
|
||||
|
||||
public void onFwCheckProgress(int val) {
|
||||
LOG.info("onFwCheckProgress: {}", val);
|
||||
downloadProgress = val;
|
||||
if (support.getHuaweiCoordinator().supportsOTADeviceRequest()) {
|
||||
try {
|
||||
SendOTAProgress progressReq = new SendOTAProgress(support, (byte) downloadProgress, (byte) 1, (byte) 0);
|
||||
progressReq.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAProgress", e);
|
||||
}
|
||||
}
|
||||
if (downloadProgress % 10 == 0) {
|
||||
setCheckingProgress(downloadProgress, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleStartQueryResponse(int respCode, byte batteryThreshold) {
|
||||
//109025
|
||||
if (respCode != 100000) {
|
||||
LOG.error("ERROR");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
if (this.state == 1) {
|
||||
this.state = 2;
|
||||
if (support.getHuaweiCoordinator().supportsOTANotify()) {
|
||||
try {
|
||||
SendOTANotifyNewVersion notifyNewVersion = new SendOTANotifyNewVersion(support, fwInfo.versionName, fwInfo.size, (byte) 1, (byte) 0);
|
||||
notifyNewVersion.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTANotifyNewVersion", e);
|
||||
}
|
||||
} else {
|
||||
startFWCheck(this.fwInfo, this.uri);
|
||||
|
||||
}
|
||||
} else if (this.state == 3) {
|
||||
try {
|
||||
SendOTADataParamsRequest sendOTADataParamsRequest = new SendOTADataParamsRequest(support);
|
||||
sendOTADataParamsRequest.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTADataParamsRequest", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Integer> decodeBitmap(byte[] bitmap) {
|
||||
if (bitmap == null || bitmap.length == 0) {
|
||||
return null;
|
||||
}
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
for (byte c : bitmap) {
|
||||
for(int i = 0; i< 8; i++) {
|
||||
ret.add((c >> i) & 1);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void handleDataChunkRequest(OTA.DataChunkRequest.Response response) {
|
||||
if (response.errorCode != null) {
|
||||
LOG.error("error");
|
||||
return;
|
||||
}
|
||||
if (currentUploadInfo == null) {
|
||||
LOG.error("No upload info");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
if (currentUploadInfo.ack) {
|
||||
try {
|
||||
// NOTE: send exactly same response back
|
||||
SendOTADataChunkRequestAck sendOTADataChunkRequestAck = new SendOTADataChunkRequestAck(support, response);
|
||||
sendOTADataChunkRequestAck.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTADataChunkRequestAck", e);
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info("ChunkRequest offset: {} length: {}", response.fileOffset, response.chunkLength);
|
||||
|
||||
if (response.wifiSend > 0) {
|
||||
LOG.error("Device reports that firmware send by WIFI. But it is not possible as not supported.");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = getFileChunk(response.fileOffset, response.chunkLength);
|
||||
if (data == null) {
|
||||
LOG.error("No data to upload");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
List<Integer> bitmap = decodeBitmap(response.bitmap);
|
||||
|
||||
LOG.info("data len: {} Bitmap: {}", data.length, bitmap);
|
||||
|
||||
try {
|
||||
SendOTAFileChunk sendOTAFileChunk = new SendOTAFileChunk(support, data, (int)response.fileOffset, currentUploadInfo.maxUnitSize, currentUploadInfo.offset, bitmap);
|
||||
sendOTAFileChunk.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAFileChunk", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleSizeReport(long size, long current) {
|
||||
LOG.info("handleSizeReport: {}, {}", size, current);
|
||||
}
|
||||
|
||||
public void handleUploadResult(int resultCode) {
|
||||
LOG.info("handleUploadResult: {}", resultCode);
|
||||
// 0 - fail, 1 - success
|
||||
if (resultCode == 0 || resultCode == 1) {
|
||||
try {
|
||||
SendOTAUploadResultAck sendOTAUploadResultAck = new SendOTAUploadResultAck(support);
|
||||
sendOTAUploadResultAck.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAUploadResultAck", e);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup();
|
||||
if(resultCode == 1) {
|
||||
setUploadComplete();
|
||||
} else if(resultCode == 0) {
|
||||
setUploadFailed();
|
||||
}
|
||||
}
|
||||
|
||||
public void handleDeviceError(int errorCode) {
|
||||
LOG.info("handleDeviceError: {}", errorCode);
|
||||
if (errorCode != 100000) {
|
||||
cleanup();
|
||||
setUploadFailed();
|
||||
}
|
||||
}
|
||||
|
||||
public void handleNotifyNewVersionResponse(int respCode) {
|
||||
if (respCode != 100000) {
|
||||
LOG.error("handleNotifyNewVersionResponse ERROR");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
if (this.state == 1) {
|
||||
if (support.getHuaweiCoordinator().supportsOTAUpdate()) {
|
||||
try {
|
||||
SendOTAStartQuery sendOTAStartQuery = new SendOTAStartQuery(support, fwInfo.versionName, (short) 256, (byte) 2, false);
|
||||
sendOTAStartQuery.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTAStartQuery", e);
|
||||
}
|
||||
} else {
|
||||
LOG.info("OTA not supported");
|
||||
}
|
||||
} else if (this.state == 2) {
|
||||
startFWCheck(this.fwInfo, this.uri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void handleGetModeResponse(int mode) {
|
||||
LOG.info("handleGetModeResponse: {}", mode);
|
||||
startFWUpload();
|
||||
}
|
||||
|
||||
public void handleDataParamsResponse(UploadInfo info) {
|
||||
LOG.info("handleDataParamsResponse: {}", info);
|
||||
this.currentUploadInfo = info;
|
||||
LOG.info("UploadInfo : waitTimeout: {}, restartTimeout: {}, unitSize: {}, interval: {}, ack: {}, offset: {}", info.waitTimeout, info.restartTimeout, info.maxUnitSize, info.interval, info.ack, info.offset);
|
||||
|
||||
try {
|
||||
SendOTASetStatus sendOTASetStatus = new SendOTASetStatus(support);
|
||||
sendOTASetStatus.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error send SendOTASetStatus", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void handleDeviceRequest(byte unkn1) {
|
||||
try {
|
||||
int code = 100000;
|
||||
if (this.state == 2) {
|
||||
code = 109021; // downloading
|
||||
} else if (this.state == 3) {
|
||||
code = 109022; // uploading
|
||||
}
|
||||
SendOTADeviceRequestReply sendOTADeviceRequestReply = new SendOTADeviceRequestReply(support, code);
|
||||
sendOTADeviceRequestReply.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Could not send response", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDeviceBusy() {
|
||||
final GBDevice device = support.getDevice();
|
||||
if (device != null && device.isConnected()) {
|
||||
device.setBusyTask(support.getContext().getString(R.string.updating_firmware));
|
||||
device.sendDeviceUpdateIntent(support.getContext());
|
||||
}
|
||||
}
|
||||
|
||||
public void unsetDeviceBusy() {
|
||||
final GBDevice device = support.getDevice();
|
||||
if (device != null && device.isConnected()) {
|
||||
if (device.isBusy()) {
|
||||
device.unsetBusyTask();
|
||||
device.sendDeviceUpdateIntent(support.getContext());
|
||||
}
|
||||
device.sendDeviceUpdateIntent(support.getContext());
|
||||
}
|
||||
}
|
||||
|
||||
public void setCheckingProgress(int progressPercent, boolean ongoing) {
|
||||
support.onUploadProgress(R.string.update_firmware_operation_check_in_progress, progressPercent, ongoing);
|
||||
}
|
||||
|
||||
public void setCheckingFailed() {
|
||||
support.onUploadProgress(R.string.update_firmware_operation_check_failed, 100, false);
|
||||
}
|
||||
|
||||
public void setUploadProgress(int progressPercent, boolean ongoing) {
|
||||
support.onUploadProgress(R.string.updatefirmwareoperation_update_in_progress, progressPercent, ongoing);
|
||||
}
|
||||
|
||||
public void setUploadComplete() {
|
||||
support.onUploadProgress(R.string.updatefirmwareoperation_update_complete_rebooting, 100, false);
|
||||
}
|
||||
|
||||
public void setUploadFailed() {
|
||||
support.onUploadProgress(R.string.updatefirmwareoperation_write_failed, 100, false);
|
||||
}
|
||||
}
|
@ -123,6 +123,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetG
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetExtendedMusicInfoParams;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetMusicInfoParams;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetNotificationConstraintsRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetOTAChangeLog;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSmartAlarmList;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWatchfaceParams;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendCameraRemoteSetupEvent;
|
||||
@ -132,6 +133,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Send
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendGpsDataRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFileUploadInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendHeartRateZonesConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendOTASetAutoUpdate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendRunPaceConfigRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendSetContactsRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendNotifyHeartRateCapabilityRequest;
|
||||
@ -257,9 +259,11 @@ public class HuaweiSupportProvider {
|
||||
|
||||
protected HuaweiDataSyncManager huaweiDataSyncManager = new HuaweiDataSyncManager(this);
|
||||
|
||||
|
||||
private HuaweiDataSyncGoals huaweiDataSyncTreeCircleGoals = null;
|
||||
|
||||
protected HuaweiOTAManager huaweiOTAManager = new HuaweiOTAManager(this);
|
||||
|
||||
|
||||
public HuaweiCoordinatorSupplier getCoordinator() {
|
||||
return ((HuaweiCoordinatorSupplier) this.gbDevice.getDeviceCoordinator());
|
||||
}
|
||||
@ -300,6 +304,10 @@ public class HuaweiSupportProvider {
|
||||
return huaweiDataSyncManager;
|
||||
}
|
||||
|
||||
public HuaweiOTAManager getHuaweiOTAManager() {
|
||||
return huaweiOTAManager;
|
||||
}
|
||||
|
||||
public HuaweiSupportProvider(HuaweiBRSupport support) {
|
||||
this.brSupport = support;
|
||||
}
|
||||
@ -844,9 +852,12 @@ public class HuaweiSupportProvider {
|
||||
initRequestQueue.add(new SetActivityReminderRequest(this));
|
||||
initRequestQueue.add(new SetTruSleepRequest(this));
|
||||
initRequestQueue.add(new GetContactsCount(this));
|
||||
initRequestQueue.add(new SendOTASetAutoUpdate(this));
|
||||
initRequestQueue.add(new GetOTAChangeLog(this));
|
||||
initRequestQueue.add(new GetEventAlarmList(this));
|
||||
initRequestQueue.add(new GetSmartAlarmList(this));
|
||||
|
||||
|
||||
// Setup the alarms if necessary
|
||||
if (!getHuaweiCoordinator().supportsChangingAlarm() && firstConnection)
|
||||
initializeAlarms();
|
||||
@ -2191,6 +2202,11 @@ public class HuaweiSupportProvider {
|
||||
LOG.info("enter onAppInstall uri: {}", uri);
|
||||
HuaweiFwHelper huaweiFwHelper = new HuaweiFwHelper(uri, getContext());
|
||||
|
||||
if(huaweiFwHelper.isFirmware) {
|
||||
huaweiOTAManager.startFwUpdate(huaweiFwHelper.fwInfo, uri);
|
||||
return;
|
||||
}
|
||||
|
||||
HuaweiUploadManager.FileUploadInfo fileInfo = new HuaweiUploadManager.FileUploadInfo();
|
||||
|
||||
if (huaweiFwHelper.isMusic()) {
|
||||
|
@ -0,0 +1,49 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class GetOTAChangeLog extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetOTAChangeLog.class);
|
||||
|
||||
public GetOTAChangeLog(HuaweiSupportProvider support) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.GetChangeLog.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean requestSupported() {
|
||||
return supportProvider.getHuaweiCoordinator().supportsOTAChangelog() &&
|
||||
supportProvider.getHuaweiCoordinator().getOtaSoftwareVersion() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
// TODO: get proper language.
|
||||
return new OTA.GetChangeLog.Request(paramsProvider, supportProvider.getHuaweiCoordinator().getOtaSoftwareVersion(), "en").serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseTypeMismatchException {
|
||||
LOG.debug("handle GetOTAChangeLog");
|
||||
if (!(receivedPacket instanceof OTA.GetChangeLog.Response))
|
||||
throw new ResponseTypeMismatchException(receivedPacket, OTA.GetChangeLog.Response.class);
|
||||
|
||||
OTA.GetChangeLog.Response resp = (OTA.GetChangeLog.Response) receivedPacket;
|
||||
|
||||
SetOTAChangeLog setOTAChangeLog = new SetOTAChangeLog(supportProvider);
|
||||
setOTAChangeLog.nextRequest(this.nextRequest);
|
||||
nextRequest(setOTAChangeLog);
|
||||
}
|
||||
}
|
@ -53,5 +53,8 @@ public class GetProductInformationRequest extends Request {
|
||||
getDevice().setFirmwareVersion(((DeviceConfig.ProductInfo.Response) receivedPacket).softwareVersion);
|
||||
getDevice().setFirmwareVersion2(((DeviceConfig.ProductInfo.Response) receivedPacket).hardwareVersion);
|
||||
getDevice().setModel(((DeviceConfig.ProductInfo.Response) receivedPacket).productModel);
|
||||
|
||||
supportProvider.getHuaweiCoordinator().setOtaSoftwareVersion(((DeviceConfig.ProductInfo.Response) receivedPacket).softwareVersion);
|
||||
supportProvider.getHuaweiCoordinator().setOtaSignatureLength(((DeviceConfig.ProductInfo.Response) receivedPacket).otaSignatureLength);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTADataChunkRequestAck extends Request {
|
||||
|
||||
private final OTA.DataChunkRequest.Response response;
|
||||
|
||||
public SendOTADataChunkRequestAck(HuaweiSupportProvider support, OTA.DataChunkRequest.Response response) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.DataChunkRequest.id;
|
||||
this.response = response;
|
||||
|
||||
this.addToResponse = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return response.serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTADataParamsRequest extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SendOTADataParamsRequest.class);
|
||||
|
||||
public SendOTADataParamsRequest(HuaweiSupportProvider support) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.DataParams.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new OTA.DataParams.Request(paramsProvider).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() {
|
||||
LOG.debug("handle SendOTADataParamsRequest");
|
||||
if (receivedPacket instanceof OTA.DataParams.Response) {
|
||||
supportProvider.getHuaweiOTAManager().handleDataParamsResponse(((OTA.DataParams.Response) receivedPacket).info);
|
||||
} else {
|
||||
LOG.error("SendOTADataParamsRequest response invalid type");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTADeviceRequestReply extends Request {
|
||||
private final int code;
|
||||
|
||||
public SendOTADeviceRequestReply(HuaweiSupportProvider support, int code) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.DeviceRequest.id;
|
||||
this.code = code;
|
||||
this.addToResponse = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new OTA.DeviceRequest.Request(paramsProvider, code).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
|
||||
public class SendOTAFileChunk extends Request {
|
||||
private final byte[] fileChunk;
|
||||
private final int offset;
|
||||
private final int unitSize;
|
||||
private final boolean addOffset;
|
||||
private final List<Integer> bitmap;
|
||||
public SendOTAFileChunk(HuaweiSupportProvider support, byte[] fileChunk, int offset, int unitSize, boolean addOffset, List<Integer> bitmap) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.NextChunkSend.id;
|
||||
this.fileChunk = fileChunk;
|
||||
this.offset = offset;
|
||||
this.unitSize = unitSize;
|
||||
this.addOffset = addOffset;
|
||||
this.bitmap = bitmap;
|
||||
|
||||
this.addToResponse = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new OTA.NextChunkSend(this.paramsProvider).serializeOTAChunk(fileChunk, offset, unitSize, addOffset,bitmap);
|
||||
} catch(HuaweiPacket.SerializeException e) {
|
||||
throw new RequestCreationException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTAGetMode extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SendOTAGetMode.class);
|
||||
|
||||
public SendOTAGetMode(HuaweiSupportProvider support) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.GetMode.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new OTA.GetMode.Request(paramsProvider).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() {
|
||||
LOG.debug("handle SendOTAGetMode");
|
||||
if (receivedPacket instanceof OTA.GetMode.Response) {
|
||||
supportProvider.getHuaweiOTAManager().handleGetModeResponse(((OTA.GetMode.Response) receivedPacket).mode);
|
||||
} else {
|
||||
LOG.error("SendOTAGetMode response invalid type");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTANotifyNewVersion extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SendOTANotifyNewVersion.class);
|
||||
|
||||
private final String newVersion;
|
||||
private final long fileSize;
|
||||
private final byte unkn1;
|
||||
private final byte unkn2;
|
||||
|
||||
public SendOTANotifyNewVersion(HuaweiSupportProvider support, String newVersion, long fileSize, byte unkn1, byte unkn2) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.NotifyNewVersion.id;
|
||||
this.newVersion = newVersion;
|
||||
this.fileSize = fileSize;
|
||||
this.unkn1 = unkn1;
|
||||
this.unkn2 = unkn2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new OTA.NotifyNewVersion.Request(paramsProvider, this.newVersion, this.fileSize, this.unkn1, this.unkn2).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() {
|
||||
LOG.debug("handle SendOTANotifyNewVersion");
|
||||
if (receivedPacket instanceof OTA.NotifyNewVersion.Response) {
|
||||
supportProvider.getHuaweiOTAManager().handleNotifyNewVersionResponse(((OTA.NotifyNewVersion.Response) receivedPacket).respCode);
|
||||
} else {
|
||||
LOG.error("SendOTANotifyNewVersion response invalid type");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTAProgress extends Request {
|
||||
|
||||
private final byte progress;
|
||||
private final byte state;
|
||||
private final byte mode;
|
||||
|
||||
public SendOTAProgress(HuaweiSupportProvider support,
|
||||
byte progress,
|
||||
byte state,
|
||||
byte mode) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.Progress.id;
|
||||
|
||||
this.progress = progress;
|
||||
this.state = state;
|
||||
this.mode = mode;
|
||||
this.addToResponse = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new OTA.Progress.Request(paramsProvider, this.progress, this.state, this.mode).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTASetAutoUpdate extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SendOTASetAutoUpdate.class);
|
||||
|
||||
public SendOTASetAutoUpdate(HuaweiSupportProvider support) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.SetAutoUpdate.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean requestSupported() {
|
||||
return supportProvider.getHuaweiCoordinator().supportsOTAAutoUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
// NOTE: always set autoupdate to false for now
|
||||
return new OTA.SetAutoUpdate.Request(paramsProvider, false).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() {
|
||||
LOG.debug("handle SendOTASetAutoUpdate");
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTASetStatus extends Request {
|
||||
|
||||
public SendOTASetStatus(HuaweiSupportProvider support) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.SetStatus.id;
|
||||
this.addToResponse = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
Byte useWifi = null;
|
||||
if(supportProvider.getHuaweiCoordinator().supportsWiFiDirect()) {
|
||||
useWifi = 0; // NOTE: do not use wifi
|
||||
}
|
||||
try {
|
||||
return new OTA.SetStatus.Request(paramsProvider, useWifi).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTAStartQuery extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SendOTAStartQuery.class);
|
||||
|
||||
private final String firmwareVersion;
|
||||
private final short fileId;
|
||||
private final byte operation;
|
||||
private final boolean add;
|
||||
|
||||
public SendOTAStartQuery(HuaweiSupportProvider support, String firmwareVersion, short fileId, byte operation, boolean add) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.StartQuery.id;
|
||||
this.firmwareVersion = firmwareVersion;
|
||||
this.fileId = fileId;
|
||||
this.operation = operation;
|
||||
this.add = add;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new OTA.StartQuery.Request(paramsProvider, this.firmwareVersion, this.fileId, this.operation, this.add).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() {
|
||||
LOG.debug("handle SendOTAStartQuery");
|
||||
if (receivedPacket instanceof OTA.StartQuery.Response) {
|
||||
supportProvider.getHuaweiOTAManager().handleStartQueryResponse(((OTA.StartQuery.Response) receivedPacket).respCode, ((OTA.StartQuery.Response) receivedPacket).batteryThreshold);
|
||||
} else {
|
||||
LOG.error("SendOTAStartQuery response invalid type");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendOTAUploadResultAck extends Request {
|
||||
|
||||
public SendOTAUploadResultAck(HuaweiSupportProvider support) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.UpdateResult.id;
|
||||
this.addToResponse = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
return new OTA.UpdateResult.Request(paramsProvider).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.OTA;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SetOTAChangeLog extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SetOTAChangeLog.class);
|
||||
|
||||
public SetOTAChangeLog(HuaweiSupportProvider support) {
|
||||
super(support);
|
||||
this.serviceId = OTA.id;
|
||||
this.commandId = OTA.SetChangeLog.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean requestSupported() {
|
||||
return supportProvider.getHuaweiCoordinator().supportsOTAChangelog() &&
|
||||
supportProvider.getHuaweiCoordinator().getOtaSoftwareVersion() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||
try {
|
||||
// TODO: currently send empty. Send real changelog. Research required.
|
||||
return new OTA.SetChangeLog.Request(paramsProvider, supportProvider.getHuaweiCoordinator().getOtaSoftwareVersion(), (byte) 0).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseTypeMismatchException {
|
||||
LOG.debug("handle SetOTAChangeLog");
|
||||
}
|
||||
}
|
@ -3600,4 +3600,6 @@
|
||||
<string name="canned_replies_not_empty">There should be at least one canned reply.</string>
|
||||
<string name="permission_disable_doze_title">Ignore battery optimizations</string>
|
||||
<string name="permission_disable_doze_summary">Allows running in the background unhindered by Android\'s battery optimizations</string>
|
||||
<string name="update_firmware_operation_check_in_progress">Checking firmware</string>
|
||||
<string name="update_firmware_operation_check_failed">Firmware check failed</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user