1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-23 23:32:23 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/cmfwatchpro/CmfDataUploader.java
2024-01-28 18:46:13 +00:00

199 lines
7.3 KiB
Java

/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.cmfwatchpro;
import android.net.Uri;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
public class CmfDataUploader implements CmfCharacteristic.Handler {
private static final Logger LOG = LoggerFactory.getLogger(CmfWatchProSupport.class);
private final CmfWatchProSupport mSupport;
private CmfFwHelper fwHelper;
public CmfDataUploader(final CmfWatchProSupport support) {
this.mSupport = support;
}
@Override
public void onCommand(final CmfCommand cmd, final byte[] payload) {
switch (cmd) {
case DATA_TRANSFER_WATCHFACE_INIT_1_REPLY:
if (payload[0] != 0x01) {
LOG.warn("Got unexpected transfer init 1 reply {}", payload[0]);
fwHelper = null;
return;
}
final ByteBuffer buf = ByteBuffer.allocate(9).order(ByteOrder.BIG_ENDIAN);
buf.put((byte) (0xa5));
buf.putInt(fwHelper.getBytes().length);
buf.putInt(new Random().nextInt()); // FIXME watchface ID?
mSupport.sendData(
"transfer watchface init 2 request",
CmfCommand.DATA_TRANSFER_WATCHFACE_INIT_2_REQUEST,
buf.array()
);
return;
case DATA_TRANSFER_AGPS_INIT_REPLY:
case DATA_TRANSFER_WATCHFACE_INIT_2_REPLY:
if (payload[0] != 0x01) {
LOG.warn("Got unexpected transfer 2 init reply {}", payload[0]);
fwHelper = null;
return;
}
setDeviceBusy();
updateProgress(0, true);
return;
case DATA_TRANSFER_WATCHFACE_FINISH_ACK_1:
handleAck1(CmfCommand.DATA_TRANSFER_WATCHFACE_FINISH_ACK_2, payload);
return;
case DATA_TRANSFER_AGPS_FINISH_ACK_1:
handleAck1(CmfCommand.DATA_TRANSFER_AGPS_FINISH_ACK_2, payload);
return;
case DATA_CHUNK_REQUEST_AGPS:
if (fwHelper == null || !fwHelper.isAgps()) {
LOG.warn("We are not sending AGPS - refusing request");
return;
}
handleChunkRequest(CmfCommand.DATA_CHUNK_REQUEST_AGPS, payload);
return;
case DATA_CHUNK_REQUEST_WATCHFACE:
if (fwHelper == null || !fwHelper.isWatchface()) {
LOG.warn("We are not sending a watchface - refusing request");
return;
}
handleChunkRequest(CmfCommand.DATA_CHUNK_WRITE_WATCHFACE, payload);
return;
}
LOG.warn("Got unknown data command {}", cmd);
}
public void onInstallApp(final Uri uri) {
if (fwHelper != null) {
LOG.warn("Already installing {}", fwHelper.getUri());
return;
}
fwHelper = new CmfFwHelper(uri, mSupport.getContext());
if (!fwHelper.isValid()) {
LOG.warn("Uri {} is not valid", uri);
fwHelper = null;
return;
}
if (fwHelper.isWatchface()) {
mSupport.sendData(
"transfer watchface init request",
CmfCommand.DATA_TRANSFER_WATCHFACE_INIT_1_REQUEST,
(byte) 0xa5
);
return;
}
LOG.warn("Unsupported fwHelper for {}", fwHelper.getUri());
fwHelper = null;
}
private void handleChunkRequest(final CmfCommand commandReply, final byte[] payload) {
final ByteBuffer buf = ByteBuffer.wrap(payload).order(ByteOrder.BIG_ENDIAN);
final int offset = buf.getInt();
final int length = buf.getInt();
final int progress = buf.get();
LOG.debug("Got chunk request: offset={}, length={}, progress={}", offset, length, progress);
final TransactionBuilder builder = mSupport.createTransactionBuilder("send chunk offset " + offset);
updateProgress(builder, progress, true);
mSupport.sendData(
"transfer watchface init request",
commandReply,
ArrayUtils.subarray(fwHelper.getBytes(), offset, offset + length)
);
}
private void handleAck1(final CmfCommand commandReply, final byte[] payload) {
if (payload[0] != 0x01) {
LOG.warn("Got unexpected transfer finish reply {}", payload[0]);
fwHelper = null;
}
LOG.debug("Got transfer finish ack 1");
unsetDeviceBusy();
updateProgress(100, false);
mSupport.sendData("transfer finish", commandReply, (byte) 0xa5);
}
private void updateProgress(final int progressPercent, boolean ongoing) {
final TransactionBuilder builder = mSupport.createTransactionBuilder("update data upload progress to " + progressPercent);
updateProgress(builder, progressPercent, ongoing);
builder.queue(mSupport.getQueue());
}
private void updateProgress(final TransactionBuilder builder, final int progressPercent, boolean ongoing) {
final int uploadMessage;
if (fwHelper != null && fwHelper.isWatchface()) {
uploadMessage = R.string.uploading_watchface;
} else {
uploadMessage = R.string.updating_firmware;
}
builder.add(new SetProgressAction(
mSupport.getContext().getString(uploadMessage),
ongoing,
progressPercent,
mSupport.getContext()
));
}
private void setDeviceBusy() {
final GBDevice device = mSupport.getDevice();
device.setBusyTask(mSupport.getContext().getString(R.string.updating_firmware));
device.sendDeviceUpdateIntent(mSupport.getContext());
}
private void unsetDeviceBusy() {
final GBDevice device = mSupport.getDevice();
if (device != null && device.isConnected()) {
if (device.isBusy()) {
device.unsetBusyTask();
device.sendDeviceUpdateIntent(mSupport.getContext());
}
device.sendDeviceUpdateIntent(mSupport.getContext());
}
}
}