Amazfit GTR 4: Fix and whitelist firmware 3.17.0.2

This commit is contained in:
José Rebelo 2022-11-12 19:02:57 +00:00 committed by Gitea
parent a1f58cff39
commit 1c93ffc3e5
25 changed files with 206 additions and 152 deletions

View File

@ -16,6 +16,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.huami; package nodomain.freeyourgadget.gadgetbridge.devices.huami;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
@ -37,11 +40,15 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiLanguageType; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiLanguageType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType;
public abstract class Huami2021Coordinator extends HuamiCoordinator { public abstract class Huami2021Coordinator extends HuamiCoordinator {
@Override
public abstract AbstractHuami2021FWInstallHandler findInstallHandler(final Uri uri, final Context context);
@Override @Override
public boolean supportsHeartRateMeasurement(final GBDevice device) { public boolean supportsHeartRateMeasurement(final GBDevice device) {
return true; return true;

View File

@ -28,7 +28,6 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuamiFirmwareInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuamiFirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType;
public abstract class HuamiFWHelper extends AbstractMiBandFWHelper { public abstract class HuamiFWHelper extends AbstractMiBandFWHelper {
@ -124,6 +123,12 @@ public abstract class HuamiFWHelper extends AbstractMiBandFWHelper {
return firmwareInfo.getFirmwareType(); return firmwareInfo.getFirmwareType();
} }
@Override
public void unsetFwBytes() {
super.unsetFwBytes();
firmwareInfo.unsetFwBytes();
}
public AbstractHuamiFirmwareInfo getFirmwareInfo() { public AbstractHuamiFirmwareInfo getFirmwareInfo() {
return firmwareInfo; return firmwareInfo;
} }

View File

@ -25,11 +25,11 @@ import androidx.annotation.NonNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
public class AmazfitBand7Coordinator extends Huami2021Coordinator { public class AmazfitBand7Coordinator extends Huami2021Coordinator {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitBand7Coordinator.class); private static final Logger LOG = LoggerFactory.getLogger(AmazfitBand7Coordinator.class);
@ -56,7 +56,7 @@ public class AmazfitBand7Coordinator extends Huami2021Coordinator {
} }
@Override @Override
public InstallHandler findInstallHandler(final Uri uri, final Context context) { public AbstractHuami2021FWInstallHandler findInstallHandler(final Uri uri, final Context context) {
final AmazfitBand7FWInstallHandler handler = new AmazfitBand7FWInstallHandler(uri, context); final AmazfitBand7FWInstallHandler handler = new AmazfitBand7FWInstallHandler(uri, context);
return handler.isValid() ? handler : null; return handler.isValid() ? handler : null;
} }

View File

@ -23,11 +23,11 @@ import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
class AmazfitBand7FWInstallHandler extends AbstractMiBandFWInstallHandler { class AmazfitBand7FWInstallHandler extends AbstractHuami2021FWInstallHandler {
AmazfitBand7FWInstallHandler(final Uri uri, final Context context) { AmazfitBand7FWInstallHandler(final Uri uri, final Context context) {
super(uri, context); super(uri, context);
} }

View File

@ -25,11 +25,11 @@ import androidx.annotation.NonNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
public class AmazfitGTR3Coordinator extends Huami2021Coordinator { public class AmazfitGTR3Coordinator extends Huami2021Coordinator {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTR3Coordinator.class); private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTR3Coordinator.class);
@ -56,7 +56,7 @@ public class AmazfitGTR3Coordinator extends Huami2021Coordinator {
} }
@Override @Override
public InstallHandler findInstallHandler(final Uri uri, final Context context) { public AbstractHuami2021FWInstallHandler findInstallHandler(final Uri uri, final Context context) {
final AmazfitGTR3FWInstallHandler handler = new AmazfitGTR3FWInstallHandler(uri, context); final AmazfitGTR3FWInstallHandler handler = new AmazfitGTR3FWInstallHandler(uri, context);
return handler.isValid() ? handler : null; return handler.isValid() ? handler : null;
} }

View File

@ -23,11 +23,11 @@ import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
class AmazfitGTR3FWInstallHandler extends AbstractMiBandFWInstallHandler { class AmazfitGTR3FWInstallHandler extends AbstractHuami2021FWInstallHandler {
AmazfitGTR3FWInstallHandler(Uri uri, Context context) { AmazfitGTR3FWInstallHandler(Uri uri, Context context) {
super(uri, context); super(uri, context);
} }

View File

@ -30,6 +30,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
public class AmazfitGTR4Coordinator extends Huami2021Coordinator { public class AmazfitGTR4Coordinator extends Huami2021Coordinator {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTR4Coordinator.class); private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTR4Coordinator.class);
@ -56,7 +57,7 @@ public class AmazfitGTR4Coordinator extends Huami2021Coordinator {
} }
@Override @Override
public InstallHandler findInstallHandler(final Uri uri, final Context context) { public AbstractHuami2021FWInstallHandler findInstallHandler(final Uri uri, final Context context) {
final AmazfitGTR4FWInstallHandler handler = new AmazfitGTR4FWInstallHandler(uri, context); final AmazfitGTR4FWInstallHandler handler = new AmazfitGTR4FWInstallHandler(uri, context);
return handler.isValid() ? handler : null; return handler.isValid() ? handler : null;
} }

View File

@ -23,11 +23,11 @@ import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
class AmazfitGTR4FWInstallHandler extends AbstractMiBandFWInstallHandler { class AmazfitGTR4FWInstallHandler extends AbstractHuami2021FWInstallHandler {
AmazfitGTR4FWInstallHandler(final Uri uri, final Context context) { AmazfitGTR4FWInstallHandler(final Uri uri, final Context context) {
super(uri, context); super(uri, context);
} }

View File

@ -25,11 +25,11 @@ import androidx.annotation.NonNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
public class AmazfitGTS3Coordinator extends Huami2021Coordinator { public class AmazfitGTS3Coordinator extends Huami2021Coordinator {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTS3Coordinator.class); private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTS3Coordinator.class);
@ -56,7 +56,7 @@ public class AmazfitGTS3Coordinator extends Huami2021Coordinator {
} }
@Override @Override
public InstallHandler findInstallHandler(final Uri uri, final Context context) { public AbstractHuami2021FWInstallHandler findInstallHandler(final Uri uri, final Context context) {
final AmazfitGTS3FWInstallHandler handler = new AmazfitGTS3FWInstallHandler(uri, context); final AmazfitGTS3FWInstallHandler handler = new AmazfitGTS3FWInstallHandler(uri, context);
return handler.isValid() ? handler : null; return handler.isValid() ? handler : null;
} }

View File

@ -23,11 +23,11 @@ import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
class AmazfitGTS3FWInstallHandler extends AbstractMiBandFWInstallHandler { class AmazfitGTS3FWInstallHandler extends AbstractHuami2021FWInstallHandler {
AmazfitGTS3FWInstallHandler(Uri uri, Context context) { AmazfitGTS3FWInstallHandler(Uri uri, Context context) {
super(uri, context); super(uri, context);
} }

View File

@ -25,11 +25,11 @@ import androidx.annotation.NonNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
public class AmazfitGTS4Coordinator extends Huami2021Coordinator { public class AmazfitGTS4Coordinator extends Huami2021Coordinator {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTS4Coordinator.class); private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTS4Coordinator.class);
@ -56,7 +56,7 @@ public class AmazfitGTS4Coordinator extends Huami2021Coordinator {
} }
@Override @Override
public InstallHandler findInstallHandler(final Uri uri, final Context context) { public AbstractHuami2021FWInstallHandler findInstallHandler(final Uri uri, final Context context) {
final AmazfitGTS4FWInstallHandler handler = new AmazfitGTS4FWInstallHandler(uri, context); final AmazfitGTS4FWInstallHandler handler = new AmazfitGTS4FWInstallHandler(uri, context);
return handler.isValid() ? handler : null; return handler.isValid() ? handler : null;
} }

View File

@ -23,11 +23,11 @@ import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
class AmazfitGTS4FWInstallHandler extends AbstractMiBandFWInstallHandler { class AmazfitGTS4FWInstallHandler extends AbstractHuami2021FWInstallHandler {
AmazfitGTS4FWInstallHandler(final Uri uri, final Context context) { AmazfitGTS4FWInstallHandler(final Uri uri, final Context context) {
super(uri, context); super(uri, context);
} }

View File

@ -25,11 +25,11 @@ import androidx.annotation.NonNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
public class MiBand7Coordinator extends Huami2021Coordinator { public class MiBand7Coordinator extends Huami2021Coordinator {
private static final Logger LOG = LoggerFactory.getLogger(MiBand7Coordinator.class); private static final Logger LOG = LoggerFactory.getLogger(MiBand7Coordinator.class);
@ -56,7 +56,7 @@ public class MiBand7Coordinator extends Huami2021Coordinator {
} }
@Override @Override
public InstallHandler findInstallHandler(final Uri uri, final Context context) { public AbstractHuami2021FWInstallHandler findInstallHandler(final Uri uri, final Context context) {
final MiBand7FWInstallHandler handler = new MiBand7FWInstallHandler(uri, context); final MiBand7FWInstallHandler handler = new MiBand7FWInstallHandler(uri, context);
return handler.isValid() ? handler : null; return handler.isValid() ? handler : null;
} }

View File

@ -23,11 +23,11 @@ import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.AbstractHuami2021FWInstallHandler;
class MiBand7FWInstallHandler extends AbstractMiBandFWInstallHandler { class MiBand7FWInstallHandler extends AbstractHuami2021FWInstallHandler {
MiBand7FWInstallHandler(Uri uri, Context context) { MiBand7FWInstallHandler(Uri uri, Context context) {
super(uri, context); super(uri, context);
} }

View File

@ -43,8 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
public abstract class AbstractMiBandFWHelper { public abstract class AbstractMiBandFWHelper {
private static final Logger LOG = LoggerFactory.getLogger(AbstractMiBandFWHelper.class); private static final Logger LOG = LoggerFactory.getLogger(AbstractMiBandFWHelper.class);
@NonNull private byte[] fw;
private final byte[] fw;
public AbstractMiBandFWHelper(Uri uri, Context context) throws IOException { public AbstractMiBandFWHelper(Uri uri, Context context) throws IOException {
UriHelper uriHelper = UriHelper.get(uri, context); UriHelper uriHelper = UriHelper.get(uri, context);
@ -104,9 +103,17 @@ public abstract class AbstractMiBandFWHelper {
@NonNull @NonNull
public byte[] getFw() { public byte[] getFw() {
if (fw == null) {
throw new IllegalStateException("fw is null");
}
return fw; return fw;
} }
public void unsetFwBytes() {
this.fw = null;
}
public boolean isFirmwareWhitelisted() { public boolean isFirmwareWhitelisted() {
for (int wlf : getWhitelistedFirmwareVersions()) { for (int wlf : getWhitelistedFirmwareVersions()) {
if (wlf == getFirmwareVersion()) { if (wlf == getFirmwareVersion()) {

View File

@ -0,0 +1,37 @@
/* Copyright (C) 2022 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
import android.content.Context;
import android.net.Uri;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
public abstract class AbstractHuami2021FWInstallHandler extends AbstractMiBandFWInstallHandler {
public AbstractHuami2021FWInstallHandler(final Uri uri, final Context context) {
super(uri, context);
}
@Override
public void onStartInstall(GBDevice device) {
// Unset the firmware bytes
// Huami2021 firmwares are large (> 130MB). With the current architecture, the update operation
// will re-read them to memory, and we run out-of-memory.
helper.unsetFwBytes();
}
}

View File

@ -26,7 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
public abstract class AbstractHuamiFirmwareInfo { public abstract class AbstractHuamiFirmwareInfo {
private final byte[] bytes; private byte[] bytes;
private final int crc16; private final int crc16;
private final int crc32; private final int crc32;
@ -78,6 +78,10 @@ public abstract class AbstractHuamiFirmwareInfo {
return firmwareType; return firmwareType;
} }
public void unsetFwBytes() {
this.bytes = null;
}
public abstract String toVersion(int crc16); public abstract String toVersion(int crc16);
public abstract boolean isGenerallyCompatibleWith(GBDevice device); public abstract boolean isGenerallyCompatibleWith(GBDevice device);

View File

@ -28,15 +28,18 @@ import java.util.Arrays;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.ZipFile; import nodomain.freeyourgadget.gadgetbridge.util.ZipFile;
import nodomain.freeyourgadget.gadgetbridge.util.ZipFileException;
public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo { public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
private static final Logger LOG = LoggerFactory.getLogger(Huami2021FirmwareInfo.class); private static final Logger LOG = LoggerFactory.getLogger(Huami2021FirmwareInfo.class);
private final String preComputedVersion;
public Huami2021FirmwareInfo(final byte[] bytes) { public Huami2021FirmwareInfo(final byte[] bytes) {
super(bytes); super(bytes);
this.preComputedVersion = preComputeVersion();
} }
/** /**
@ -44,11 +47,6 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
*/ */
public abstract String deviceName(); public abstract String deviceName();
/**
* The expected firmware header bytes, to search on firmware.bin in order to determine compatibility.
*/
public abstract byte[] getExpectedFirmwareHeader();
@Override @Override
protected HuamiFirmwareType determineFirmwareType(final byte[] bytes) { protected HuamiFirmwareType determineFirmwareType(final byte[] bytes) {
if (ArrayUtils.equals(bytes, UIHHContainer.UIHH_HEADER, 0)) { if (ArrayUtils.equals(bytes, UIHHContainer.UIHH_HEADER, 0)) {
@ -70,7 +68,7 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
UIHHContainer.FileEntry uihhFirmwareZipFile = null; UIHHContainer.FileEntry uihhFirmwareZipFile = null;
boolean hasChangelog = false; boolean hasChangelog = false;
for (final UIHHContainer.FileEntry file : uihh.getFiles()) { for (final UIHHContainer.FileEntry file : uihh.getFiles()) {
switch(file.getType()) { switch (file.getType()) {
case FIRMWARE_ZIP: case FIRMWARE_ZIP:
uihhFirmwareZipFile = file; uihhFirmwareZipFile = file;
continue; continue;
@ -83,7 +81,14 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
} }
if (uihhFirmwareZipFile != null && hasChangelog) { if (uihhFirmwareZipFile != null && hasChangelog) {
byte[] firmwareBin = ZipFile.tryReadFileQuick(uihhFirmwareZipFile.getContent(), "META/firmware.bin"); final ZipFile zipFile = new ZipFile(uihhFirmwareZipFile.getContent());
final byte[] firmwareBin;
try {
firmwareBin = zipFile.getFileFromZip("META/firmware.bin");
} catch (final ZipFileException e) {
LOG.error("Failed to read zip from UIHH", e);
return HuamiFirmwareType.INVALID;
}
if (isCompatibleFirmwareBin(firmwareBin)) { if (isCompatibleFirmwareBin(firmwareBin)) {
return HuamiFirmwareType.FIRMWARE_UIHH_2021_ZIP_WITH_CHANGELOG; return HuamiFirmwareType.FIRMWARE_UIHH_2021_ZIP_WITH_CHANGELOG;
@ -94,13 +99,33 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
} }
private HuamiFirmwareType handleZipPackage(byte[] bytes) { private HuamiFirmwareType handleZipPackage(byte[] bytes) {
final byte[] firmwareBin = ZipFile.tryReadFileQuick(bytes, "META/firmware.bin"); final ZipFile zipFile = new ZipFile(bytes);
final byte[] firmwareBin;
try {
firmwareBin = zipFile.getFileFromZip("META/firmware.bin");
} catch (final ZipFileException e) {
LOG.error("Failed to get firmware.bin from zip file", e);
return HuamiFirmwareType.FIRMWARE;
}
if (isCompatibleFirmwareBin(firmwareBin)) { if (isCompatibleFirmwareBin(firmwareBin)) {
return HuamiFirmwareType.FIRMWARE; return HuamiFirmwareType.FIRMWARE;
} }
final String appType = getAppType(); final JSONObject appJson = getAppJson(zipFile);
switch(appType) { if (appJson == null) {
return HuamiFirmwareType.INVALID;
}
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": case "watchface":
return HuamiFirmwareType.WATCHFACE; return HuamiFirmwareType.WATCHFACE;
case "app": case "app":
@ -119,27 +144,35 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
return crcMapVersion; return crcMapVersion;
} }
switch (firmwareType) { return preComputedVersion;
case FIRMWARE_UIHH_2021_ZIP_WITH_CHANGELOG: }
final UIHHContainer uihh = UIHHContainer.fromRawBytes(getBytes());
if (uihh == null) { public String preComputeVersion() {
return null; try {
} switch (firmwareType) {
return getFirmwareVersion(uihh.getFile(UIHHContainer.FileType.FIRMWARE_ZIP)); case FIRMWARE_UIHH_2021_ZIP_WITH_CHANGELOG:
case FIRMWARE: final UIHHContainer uihh = UIHHContainer.fromRawBytes(getBytes());
return getFirmwareVersion(getBytes()); if (uihh == null) {
case WATCHFACE: return null;
final String watchfaceName = getAppName(); }
if (watchfaceName == null) { return getFirmwareVersion(uihh.getFile(UIHHContainer.FileType.FIRMWARE_ZIP));
return "(unknown watchface)"; case FIRMWARE:
} return getFirmwareVersion(getBytes());
return String.format("%s (watchface)", watchfaceName); case WATCHFACE:
case APP: final String watchfaceName = getAppName(new ZipFile(getBytes()));
final String appName = getAppName(); if (watchfaceName == null) {
if (appName == null) { return "(unknown watchface)";
return "(unknown app)"; }
} return String.format("%s (watchface)", watchfaceName);
return String.format("%s (app)", appName); case APP:
final String appName = getAppName(new ZipFile(getBytes()));
if (appName == null) {
return "(unknown app)";
}
return String.format("%s (app)", appName);
}
} catch (final Exception e) {
LOG.error("Failed to pre compute version", e);
} }
return null; return null;
@ -150,7 +183,7 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
throw new IllegalStateException("Can only repack FIRMWARE"); throw new IllegalStateException("Can only repack FIRMWARE");
} }
final UIHHContainer uihh = packFirmwareInUIHH(); final UIHHContainer uihh = packFirmwareInUIHH(getBytes());
try { try {
final Constructor<? extends Huami2021FirmwareInfo> constructor = this.getClass().getConstructor(byte[].class); final Constructor<? extends Huami2021FirmwareInfo> constructor = this.getClass().getConstructor(byte[].class);
@ -160,7 +193,7 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
} }
} }
private UIHHContainer packFirmwareInUIHH() { private static UIHHContainer packFirmwareInUIHH(final byte[] zipBytes) {
final UIHHContainer uihh = new UIHHContainer(); final UIHHContainer uihh = new UIHHContainer();
final byte[] timestampBytes = BLETypeConversions.fromUint32((int) (System.currentTimeMillis() / 1000L)); final byte[] timestampBytes = BLETypeConversions.fromUint32((int) (System.currentTimeMillis() / 1000L));
final String changelogText = "Unknown changelog"; final String changelogText = "Unknown changelog";
@ -168,23 +201,18 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
timestampBytes, timestampBytes,
changelogText.getBytes(StandardCharsets.UTF_8) changelogText.getBytes(StandardCharsets.UTF_8)
); );
uihh.addFile(UIHHContainer.FileType.FIRMWARE_ZIP, getBytes()); uihh.addFile(UIHHContainer.FileType.FIRMWARE_ZIP, zipBytes);
uihh.addFile(UIHHContainer.FileType.FIRMWARE_CHANGELOG, changelogBytes); uihh.addFile(UIHHContainer.FileType.FIRMWARE_CHANGELOG, changelogBytes);
return uihh; return uihh;
} }
private boolean isCompatibleFirmwareBin(final byte[] firmwareBin) { private boolean isCompatibleFirmwareBin(final byte[] firmwareBin) {
if (firmwareBin == null) { if (firmwareBin == null) {
LOG.error("firmware bin is null");
return false; return false;
} }
if (!ArrayUtils.equals(firmwareBin, getExpectedFirmwareHeader(), 0)) { if (!searchString(firmwareBin, deviceName())) {
LOG.warn("Unexpected firmware header: {}", GB.hexdump(Arrays.copyOfRange(firmwareBin, 0, getExpectedFirmwareHeader().length + 3)));
return false;
}
// On the MB7, this only works for firmwares > 1.8.5.1, not for any older firmware
if (!searchString32BitAligned(firmwareBin, deviceName() + "\0")) {
LOG.warn("Failed to find {} in fwBytes", deviceName()); LOG.warn("Failed to find {} in fwBytes", deviceName());
return false; return false;
} }
@ -192,11 +220,13 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
return true; return true;
} }
public String getFirmwareVersion(final byte[] fwbytes) { public static String getFirmwareVersion(final byte[] fwBytes) {
final byte[] firmwareBin = ZipFile.tryReadFileQuick(fwbytes, "META/firmware.bin"); final ZipFile zipFile = new ZipFile(fwBytes);
final byte[] firmwareBin;
if (firmwareBin == null) { try {
LOG.warn("Failed to read firmware.bin"); firmwareBin = zipFile.getFileFromZip("META/firmware.bin");
} catch (final ZipFileException e) {
LOG.error("Failed to get firmware.bin from zip", e);
return null; return null;
} }
@ -225,10 +255,30 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
return new String(Arrays.copyOfRange(firmwareBin, startIdx, endIdx)); return new String(Arrays.copyOfRange(firmwareBin, startIdx, endIdx));
} }
public String getAppName() { public String getAppName(final ZipFile zipFile) {
final byte[] appJsonBin = ZipFile.tryReadFileQuick(getBytes(), "app.json"); // TODO check i18n section?
if (appJsonBin == null) { // TODO Show preview icon?
LOG.warn("Failed to get app.json from zip");
final JSONObject appJson = getAppJson(zipFile);
if (appJson == null) {
return null;
}
try {
return appJson.getJSONObject("app").getString("appName");
} catch (final Exception e) {
LOG.error("Failed to get appName from app.json", e);
}
return null;
}
private static JSONObject getAppJson(final ZipFile zipFile) {
final byte[] appJsonBin;
try {
appJsonBin = zipFile.getFileFromZip("app.json");
} catch (final ZipFileException e) {
LOG.error("Failed to read app.json", e);
return null; return null;
} }
@ -236,12 +286,7 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
final String appJsonString = new String(appJsonBin, StandardCharsets.UTF_8) final String appJsonString = new String(appJsonBin, StandardCharsets.UTF_8)
// Remove UTF-8 BOM if present // Remove UTF-8 BOM if present
.replace("\uFEFF", ""); .replace("\uFEFF", "");
final JSONObject jsonObject = new JSONObject(appJsonString); return new JSONObject(appJsonString);
// TODO check i18n section?
// TODO Show preview icon?
final String appName = jsonObject.getJSONObject("app").getString("appName");
return String.format("%s", appName);
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Failed to parse app.json", e); LOG.error("Failed to parse app.json", e);
} }
@ -249,23 +294,22 @@ public abstract class Huami2021FirmwareInfo extends AbstractHuamiFirmwareInfo {
return null; return null;
} }
public String getAppType() { public static boolean searchString(final byte[] fwBytes, final String str) {
final byte[] appJsonBin = ZipFile.tryReadFileQuick(getBytes(), "app.json"); final byte[] strBytes = (str + "\0").getBytes(StandardCharsets.UTF_8);
if (appJsonBin == null) {
LOG.warn("Failed to get app.json from zip"); for (int i = 0; i < fwBytes.length - strBytes.length + 1; i++) {
return null; boolean found = true;
for (int j = 0; j < strBytes.length; j++) {
if (fwBytes[i + j] != strBytes[j]) {
found = false;
break;
}
}
if (found) {
return true;
}
} }
try { return false;
final String appJsonString = new String(appJsonBin, StandardCharsets.UTF_8)
// Remove UTF-8 BOM if present
.replace("\uFEFF", "");
final JSONObject jsonObject = new JSONObject(appJsonString);
return jsonObject.getJSONObject("app").getString("appType");
} catch (final Exception e) {
LOG.error("Failed to parse app.json", e);
}
return null;
} }
} }

View File

@ -37,11 +37,6 @@ public class AmazfitBand7FirmwareInfo extends Huami2021FirmwareInfo {
return HuamiConst.AMAZFIT_BAND7_NAME; return HuamiConst.AMAZFIT_BAND7_NAME;
} }
@Override
public byte[] getExpectedFirmwareHeader() {
return new byte[]{0x51, 0x71};
}
@Override @Override
public boolean isGenerallyCompatibleWith(final GBDevice device) { public boolean isGenerallyCompatibleWith(final GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.AMAZFITBAND7; return isHeaderValid() && device.getType() == DeviceType.AMAZFITBAND7;

View File

@ -43,11 +43,6 @@ public class AmazfitGTR3FirmwareInfo extends Huami2021FirmwareInfo {
return HuamiConst.AMAZFIT_GTR3_NAME; return HuamiConst.AMAZFIT_GTR3_NAME;
} }
@Override
public byte[] getExpectedFirmwareHeader() {
return new byte[]{0x00, 0x00, 0x50}; // Probably bogus, only checked against 2 firmware files
}
@Override @Override
public boolean isGenerallyCompatibleWith(final GBDevice device) { public boolean isGenerallyCompatibleWith(final GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTR3; return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTR3;

View File

@ -27,6 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Firmw
public class AmazfitGTR4FirmwareInfo extends Huami2021FirmwareInfo { public class AmazfitGTR4FirmwareInfo extends Huami2021FirmwareInfo {
private static final Map<Integer, String> crcToVersion = new HashMap<Integer, String>() {{ private static final Map<Integer, String> crcToVersion = new HashMap<Integer, String>() {{
// firmware // firmware
put(1699, "3.17.0.2");
}}; }};
public AmazfitGTR4FirmwareInfo(final byte[] bytes) { public AmazfitGTR4FirmwareInfo(final byte[] bytes) {
@ -38,11 +39,6 @@ public class AmazfitGTR4FirmwareInfo extends Huami2021FirmwareInfo {
return HuamiConst.AMAZFIT_GTR4_NAME; return HuamiConst.AMAZFIT_GTR4_NAME;
} }
@Override
public byte[] getExpectedFirmwareHeader() {
return new byte[]{(byte) 0x51, (byte) 0x71, (byte) 0x9c}; // Probably bogus, only checked against 1 firmware files
}
@Override @Override
public boolean isGenerallyCompatibleWith(final GBDevice device) { public boolean isGenerallyCompatibleWith(final GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTR4; return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTR4;

View File

@ -43,11 +43,6 @@ public class AmazfitGTS3FirmwareInfo extends Huami2021FirmwareInfo {
return HuamiConst.AMAZFIT_GTS3_NAME; return HuamiConst.AMAZFIT_GTS3_NAME;
} }
@Override
public byte[] getExpectedFirmwareHeader() {
return new byte[]{0x00, 0x00, 0x50}; // Probably bogus, only checked against 2 firmware files
}
@Override @Override
public boolean isGenerallyCompatibleWith(final GBDevice device) { public boolean isGenerallyCompatibleWith(final GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTS3; return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTS3;

View File

@ -38,11 +38,6 @@ public class AmazfitGTS4FirmwareInfo extends Huami2021FirmwareInfo {
return HuamiConst.AMAZFIT_GTS4_NAME; return HuamiConst.AMAZFIT_GTS4_NAME;
} }
@Override
public byte[] getExpectedFirmwareHeader() {
return new byte[]{(byte) 0x51, (byte) 0x71, (byte) 0x9c}; // Probably bogus, only checked against 1 firmware files
}
@Override @Override
public boolean isGenerallyCompatibleWith(final GBDevice device) { public boolean isGenerallyCompatibleWith(final GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTS4; return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTS4;

View File

@ -43,11 +43,6 @@ public class MiBand7FirmwareInfo extends Huami2021FirmwareInfo {
return HuamiConst.XIAOMI_SMART_BAND7_NAME; return HuamiConst.XIAOMI_SMART_BAND7_NAME;
} }
@Override
public byte[] getExpectedFirmwareHeader() {
return new byte[]{0x51, 0x71};
}
@Override @Override
public boolean isGenerallyCompatibleWith(final GBDevice device) { public boolean isGenerallyCompatibleWith(final GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.MIBAND7; return isHeaderValid() && device.getType() == DeviceType.MIBAND7;

View File

@ -79,28 +79,6 @@ public class ZipFile {
} }
} }
/**
* Tries to obtain file from ZIP file without much hassle, but is not safe.<br>
* Please only use this in place of old code where correctness of the result is checked only later on.<br>
* Use getFileFromZip of ZipFile instance instead.
* @param zipBytes
* @param path Path of the file in the ZIP file.
* @return Contents of requested file or null.
*/
@Deprecated
@Nullable
public static byte[] tryReadFileQuick(final byte[] zipBytes, final String path) {
try {
return new ZipFile(zipBytes).getFileFromZip(path);
} catch (ZipFileException e) {
LOG.error("Quick ZIP reading failed.", e);
} catch (Exception e) {
LOG.error("Unable to close ZipFile.", e);
}
return null;
}
private static byte[] readAllBytes(final InputStream is) throws IOException { private static byte[] readAllBytes(final InputStream is) throws IOException {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ByteArrayOutputStream buffer = new ByteArrayOutputStream();