mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-29 05:16:51 +01:00
Xiaomi: Refactor to install firmware (untested)
This commit is contained in:
parent
c47e830056
commit
29c183b88a
@ -14,7 +14,7 @@
|
||||
|
||||
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.devices.xiaomi.miband8;
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.xiaomi;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
@ -36,9 +36,12 @@ public class XiaomiFWHelper {
|
||||
private final Uri uri;
|
||||
private byte[] fw;
|
||||
private boolean valid;
|
||||
private boolean typeFirmware;
|
||||
private boolean typeWatchface;
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String version;
|
||||
|
||||
public XiaomiFWHelper(final Uri uri, final Context context) {
|
||||
this.uri = uri;
|
||||
@ -65,15 +68,23 @@ public class XiaomiFWHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
valid = parseFirmware();
|
||||
parseBytes();
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public boolean isWatchface() {
|
||||
return typeWatchface;
|
||||
}
|
||||
|
||||
public boolean isFirmware() {
|
||||
return typeFirmware;
|
||||
}
|
||||
|
||||
public String getDetails() {
|
||||
return name != null ? name : "UNKNOWN WATCHFACE";
|
||||
return name != null ? name : (version != null ? version : "UNKNOWN");
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
@ -88,11 +99,29 @@ public class XiaomiFWHelper {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void unsetFwBytes() {
|
||||
this.fw = null;
|
||||
}
|
||||
|
||||
private boolean parseFirmware() {
|
||||
private void parseBytes() {
|
||||
if (parseAsWatchface()) {
|
||||
assert id != null;
|
||||
valid = true;
|
||||
typeWatchface = true;
|
||||
} else if (parseAsFirmware()) {
|
||||
assert version != null;
|
||||
valid = true;
|
||||
typeFirmware = true;
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean parseAsWatchface() {
|
||||
if (fw[0] != (byte) 0x5A || fw[1] != (byte) 0xA5) {
|
||||
LOG.warn("File header not a watchface");
|
||||
return false;
|
||||
@ -120,4 +149,9 @@ public class XiaomiFWHelper {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean parseAsFirmware() {
|
||||
// TODO parse and set version
|
||||
return false;
|
||||
}
|
||||
}
|
@ -22,7 +22,6 @@ import android.net.Uri;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.miband8.XiaomiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
|
||||
@ -63,8 +62,17 @@ public class XiaomiInstallHandler implements InstallHandler {
|
||||
}
|
||||
|
||||
final GenericItem installItem = new GenericItem();
|
||||
installItem.setIcon(R.drawable.ic_watchface);
|
||||
installItem.setName(mContext.getString(R.string.kind_watchface));
|
||||
if (helper.isWatchface()) {
|
||||
installItem.setIcon(R.drawable.ic_watchface);
|
||||
installItem.setName(mContext.getString(R.string.kind_watchface));
|
||||
} else if (helper.isFirmware()) {
|
||||
installItem.setIcon(R.drawable.ic_firmware);
|
||||
installItem.setName(mContext.getString(R.string.kind_firmware));
|
||||
} else {
|
||||
installItem.setIcon(R.drawable.ic_device_unknown);
|
||||
installItem.setName(mContext.getString(R.string.kind_invalid));
|
||||
}
|
||||
|
||||
installItem.setDetails(helper.getDetails());
|
||||
|
||||
installActivity.setInfoText(mContext.getString(R.string.firmware_install_warning, "(unknown)"));
|
||||
|
@ -27,7 +27,6 @@ import android.net.Uri;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@ -35,6 +34,7 @@ import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
@ -319,8 +319,20 @@ public abstract class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onInstallApp(final Uri uri) {
|
||||
// TODO distinguish between fw and watchface
|
||||
watchfaceService.installWatchface(uri);
|
||||
final XiaomiFWHelper fwHelper = new XiaomiFWHelper(uri, getContext());
|
||||
|
||||
if (!fwHelper.isValid()) {
|
||||
LOG.warn("Uri {} is not valid", uri);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fwHelper.isFirmware()) {
|
||||
systemService.installFirmware(fwHelper);
|
||||
} else if (fwHelper.isWatchface()) {
|
||||
watchfaceService.installWatchface(fwHelper);
|
||||
} else {
|
||||
LOG.warn("Unknown fwhelper for {}", uri);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,6 +30,7 @@ import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
@ -38,15 +39,21 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdateDevi
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiPreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
public class XiaomiSystemService extends AbstractXiaomiService {
|
||||
public class XiaomiSystemService extends AbstractXiaomiService implements XiaomiDataUploadService.Callback {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XiaomiSystemService.class);
|
||||
|
||||
public static final int COMMAND_TYPE = 2;
|
||||
@ -54,6 +61,7 @@ public class XiaomiSystemService extends AbstractXiaomiService {
|
||||
public static final int CMD_BATTERY = 1;
|
||||
public static final int CMD_DEVICE_INFO = 2;
|
||||
public static final int CMD_CLOCK = 3;
|
||||
public static final int CMD_FIRMWARE_INSTALL = 5;
|
||||
public static final int CMD_LANGUAGE = 6;
|
||||
public static final int CMD_PASSWORD_GET = 9;
|
||||
public static final int CMD_FIND_PHONE = 17;
|
||||
@ -63,6 +71,9 @@ public class XiaomiSystemService extends AbstractXiaomiService {
|
||||
public static final int CMD_DISPLAY_ITEMS_SET = 30;
|
||||
public static final int CMD_CHARGER = 79;
|
||||
|
||||
// Not null if we're installing a firmware
|
||||
private XiaomiFWHelper fwHelper = null;
|
||||
|
||||
public XiaomiSystemService(final XiaomiSupport support) {
|
||||
super(support);
|
||||
}
|
||||
@ -86,6 +97,18 @@ public class XiaomiSystemService extends AbstractXiaomiService {
|
||||
case CMD_BATTERY:
|
||||
handleBattery(cmd.getSystem().getPower().getBattery());
|
||||
return;
|
||||
case CMD_FIRMWARE_INSTALL:
|
||||
final int installStatus = cmd.getSystem().getFirmwareInstallResponse().getStatus();
|
||||
if (installStatus != 0) {
|
||||
LOG.warn("Invalid firmware install status {} for {}", installStatus, fwHelper.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.debug("Firmware install status 0, uploading");
|
||||
setDeviceBusy();
|
||||
getSupport().getDataUploader().setCallback(this);
|
||||
getSupport().getDataUploader().requestUpload(XiaomiDataUploadService.TYPE_FIRMWARE, fwHelper.getBytes());
|
||||
return;
|
||||
case CMD_PASSWORD_GET:
|
||||
handlePassword(cmd.getSystem().getPassword());
|
||||
return;
|
||||
@ -400,6 +423,82 @@ public class XiaomiSystemService extends AbstractXiaomiService {
|
||||
);
|
||||
}
|
||||
|
||||
public void installFirmware(final XiaomiFWHelper fwHelper) {
|
||||
assert fwHelper.isValid();
|
||||
assert fwHelper.isFirmware();
|
||||
|
||||
this.fwHelper = fwHelper;
|
||||
|
||||
getSupport().sendCommand(
|
||||
"install firmware " + fwHelper.getVersion(),
|
||||
XiaomiProto.Command.newBuilder()
|
||||
.setType(COMMAND_TYPE)
|
||||
.setSubtype(CMD_FIRMWARE_INSTALL)
|
||||
.setSystem(XiaomiProto.System.newBuilder().setFirmwareInstallRequest(
|
||||
XiaomiProto.FirmwareInstallRequest.newBuilder()
|
||||
.setUnknown1(0)
|
||||
.setUnknown2(0)
|
||||
.setVersion(fwHelper.getVersion())
|
||||
.setMd5(GB.hexdump(CheckSums.md5(fwHelper.getBytes())).toLowerCase(Locale.ROOT))
|
||||
))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
private void setDeviceBusy() {
|
||||
final GBDevice device = getSupport().getDevice();
|
||||
device.setBusyTask(getSupport().getContext().getString(R.string.updating_firmware));
|
||||
device.sendDeviceUpdateIntent(getSupport().getContext());
|
||||
}
|
||||
|
||||
private void unsetDeviceBusy() {
|
||||
final GBDevice device = getSupport().getDevice();
|
||||
if (device != null && device.isConnected()) {
|
||||
if (device.isBusy()) {
|
||||
device.unsetBusyTask();
|
||||
device.sendDeviceUpdateIntent(getSupport().getContext());
|
||||
}
|
||||
device.sendDeviceUpdateIntent(getSupport().getContext());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUploadFinish(final boolean success) {
|
||||
LOG.debug("Firmware upload finished: {}", success);
|
||||
|
||||
getSupport().getDataUploader().setCallback(null);
|
||||
|
||||
final String notificationMessage = success ?
|
||||
getSupport().getContext().getString(R.string.updatefirmwareoperation_update_complete) :
|
||||
getSupport().getContext().getString(R.string.updatefirmwareoperation_write_failed);
|
||||
|
||||
GB.updateInstallNotification(notificationMessage, false, 100, getSupport().getContext());
|
||||
|
||||
unsetDeviceBusy();
|
||||
|
||||
if (success) {
|
||||
// TODO do we need to reboot?
|
||||
}
|
||||
|
||||
fwHelper = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUploadProgress(final int progressPercent) {
|
||||
try {
|
||||
final TransactionBuilder builder = getSupport().createTransactionBuilder("send data upload progress");
|
||||
builder.add(new SetProgressAction(
|
||||
getSupport().getContext().getString(R.string.updatefirmwareoperation_update_in_progress),
|
||||
true,
|
||||
progressPercent,
|
||||
getSupport().getContext()
|
||||
));
|
||||
builder.queue(getSupport().getQueue());
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to update progress notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<String, String> DISPLAY_ITEM_NAMES = new HashMap<String, String>() {{
|
||||
put("today_act", "Stats");
|
||||
put("sport", "Workout");
|
||||
|
@ -16,8 +16,6 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -29,7 +27,7 @@ import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.miband8.XiaomiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
|
||||
@ -190,13 +188,11 @@ public class XiaomiWatchfaceService extends AbstractXiaomiService implements Xia
|
||||
);
|
||||
}
|
||||
|
||||
public void installWatchface(final Uri uri) {
|
||||
fwHelper = new XiaomiFWHelper(uri, getSupport().getContext());
|
||||
if (!fwHelper.isValid()) {
|
||||
fwHelper = null;
|
||||
LOG.warn("watchface is not valid");
|
||||
return;
|
||||
}
|
||||
public void installWatchface(final XiaomiFWHelper fwHelper) {
|
||||
assert fwHelper.isValid();
|
||||
assert fwHelper.isWatchface();
|
||||
|
||||
this.fwHelper = fwHelper;
|
||||
|
||||
getSupport().sendCommand(
|
||||
"install watchface " + fwHelper.getId(),
|
||||
|
@ -88,6 +88,10 @@ message System {
|
||||
// 2, 34
|
||||
optional DoNotDisturb dndStatus = 11;
|
||||
|
||||
// 2, 5
|
||||
optional FirmwareInstallRequest firmwareInstallRequest = 16;
|
||||
optional FirmwareInstallResponse firmwareInstallResponse = 17;
|
||||
|
||||
// 2, 9 get | 2, 21 set
|
||||
optional Password password = 19;
|
||||
|
||||
@ -236,6 +240,17 @@ message DoNotDisturb2 {
|
||||
message DndSync {
|
||||
}
|
||||
|
||||
message FirmwareInstallRequest {
|
||||
optional uint32 unknown1 = 1; // 0
|
||||
optional uint32 unknown2 = 2; // 0
|
||||
optional string version = 3;
|
||||
optional string md5 = 4;
|
||||
}
|
||||
|
||||
message FirmwareInstallResponse {
|
||||
optional uint32 status = 1; // 0
|
||||
}
|
||||
|
||||
message Password {
|
||||
optional uint32 state = 1; // 1 disabled, 2 enabled
|
||||
optional string password = 2;
|
||||
|
Loading…
Reference in New Issue
Block a user