Huawei: initial screenshots support

This commit is contained in:
Me7c7 2025-02-13 20:04:14 +02:00
parent c6bbda5297
commit eaf6be424d
19 changed files with 1055 additions and 58 deletions

View File

@ -649,6 +649,9 @@ public class HuaweiCoordinator {
return supportsCommandForService(0x09, 0x0f);
}
public boolean supportDefaultSwitch() {
return supportsCommandForService(0x01, 0x21);
}
public boolean supportsExternalCalendarService() {
if (supportsExpandCapability())
@ -780,7 +783,13 @@ public class HuaweiCoordinator {
return supportsExpandCapability(147);
return false;
}
public boolean supportsReverseCapabilities() {
if (supportsExpandCapability())
return supportsExpandCapability(182);
return false;
}
public boolean supportsPromptPushMessage () {
// do not ask for capabilities under specific condition
// if (deviceType == 10 && deviceVersion == 73617766697368 && deviceSoftVersion == 372E312E31) -> leo device

View File

@ -19,6 +19,8 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.HUAWEI_MAGIC;
import androidx.annotation.NonNull;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@ -455,6 +457,8 @@ public class HuaweiPacket {
return new DeviceConfig.DeviceStatus.Response(paramsProvider).fromPacket(this);
case DeviceConfig.DndLiftWristType.id:
return new DeviceConfig.DndLiftWristType.Response(paramsProvider).fromPacket(this);
case DeviceConfig.GetDefaultSwitch.id:
return new DeviceConfig.GetDefaultSwitch.Response(paramsProvider).fromPacket(this);
case DeviceConfig.HiChain.id:
return new DeviceConfig.HiChain.Response(paramsProvider).fromPacket(this);
case DeviceConfig.PinCode.id:
@ -471,6 +475,8 @@ public class HuaweiPacket {
return new DeviceConfig.SecurityNegotiation.Response(paramsProvider).fromPacket(this);
case DeviceConfig.WearStatus.id:
return new DeviceConfig.WearStatus.Response(paramsProvider).fromPacket(this);
case DeviceConfig.ReverseCapabilities.id:
return new DeviceConfig.ReverseCapabilities.Response(paramsProvider).fromPacket(this);
// Camera remote has same ID as DeviceConfig
case CameraRemote.CameraRemoteStatus.id:
@ -677,10 +683,14 @@ public class HuaweiPacket {
switch (this.commandId) {
case FileDownloadService2C.FileDownloadInit.id:
return new FileDownloadService2C.FileDownloadInit.Response(paramsProvider).fromPacket(this);
case FileDownloadService2C.FileRequestHash.id:
return new FileDownloadService2C.FileRequestHash.Response(paramsProvider).fromPacket(this);
case FileDownloadService2C.FileInfo.id:
return new FileDownloadService2C.FileInfo.Response(paramsProvider).fromPacket(this);
case FileDownloadService2C.BlockResponse.id:
return new FileDownloadService2C.BlockResponse(paramsProvider).fromPacket(this);
case FileDownloadService2C.IncomingInitRequest.id:
return new FileDownloadService2C.IncomingInitRequest.Response(paramsProvider).fromPacket(this);
default:
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
return this;
@ -1090,6 +1100,7 @@ public class HuaweiPacket {
return Objects.equals(tlv, that.tlv);
}
@NonNull
@Override
public String toString() {
return "HuaweiPacket{" +

View File

@ -96,7 +96,7 @@ public class DeviceConfig {
this.interval = this.tlv.getShort(0x04);
System.arraycopy(this.tlv.getBytes(0x05), 2, this.serverNonce, 0, 16);
this.authVersion = (byte)this.tlv.getBytes(0x05)[1];
this.authVersion = this.tlv.getBytes(0x05)[1];
if (this.tlv.contains(0x07))
this.deviceSupportType = this.tlv.getByte(0x07);
@ -116,15 +116,17 @@ public class DeviceConfig {
public static class SupportedServices {
public static final byte id = 0x02;
// device should always support service 0x01
// notDeviceCapabilities = 0x1C, 0x1E, 0x1F, 0x28, 0x29, 0x2C, 0x2F, 0x31
// but services = 0x1E, 0x28, 0x2C, 0x31
// service 0x21 depends on MiddleWear support
public static final byte[] knownSupportedServices = new byte[] {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1D, 0x20,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2A, 0x2B, 0x2D, 0x2E,
0x30, 0x32, 0x33, 0x34, 0x35
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1D, 0x20,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2A, 0x2B, 0x2D, 0x2E,
0x30, 0x32, 0x33, 0x34, 0x35
};
public static class Request extends HuaweiPacket {
@ -803,6 +805,15 @@ public class DeviceConfig {
switch (b) {
case 0x0:
break;
case 0x2:
this.tlv.put(b); // Force phone manufactures to ""
break;
case 0x4:
this.tlv.put(b); // Force phone model to ""
break;
case 0x8:
this.tlv.put(b, "14"); // Force android version to "14"
break;
case 0xf:
break;
case 0x11:
@ -964,6 +975,35 @@ public class DeviceConfig {
// TODO: set (earphone) double tap action 0x1f
// TODO: get (earphone) double tap action 0x20
public static class GetDefaultSwitch {
public static final int id = 0x21;
public static class Request extends HuaweiPacket {
public Request(HuaweiPacket.ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01);
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
}
@Override
public void parseTlv() throws ParseException {
}
}
}
public static class HiChain {
public static final int id = 0x28;
@ -1467,28 +1507,27 @@ public class DeviceConfig {
this.serviceId = DeviceConfig.id;
this.commandId = id;
int timestamp = (int) (System.currentTimeMillis() / 1000);
long timestamp = System.currentTimeMillis();
HuaweiTLV software = new HuaweiTLV()
.put(0x03, "software_update_service_statement")
.put(0x04, 0x01)
.put(0x04, (byte)0x01)
.put(0x05, "20230508-20230508-0-0")
.put(0x06, timestamp);
.put(0x06, String.valueOf(timestamp));
HuaweiTLV device_information = new HuaweiTLV()
.put(0x03, "device_information_management")
.put(0x04,0x01)
.put(0x04,(byte)0x01)
.put(0x05, "20230508-20230508-0-0")
.put(0x06,timestamp);
.put(0x06,String.valueOf(timestamp));
HuaweiTLV user_license = new HuaweiTLV()
.put(0x03, "user_license_agreement")
.put(0x04,0x01)
.put(0x04,(byte)0x01)
.put(0x05, "20230508-20230508-0-0")
.put(0x06,timestamp);
.put(0x06,String.valueOf(timestamp));
HuaweiTLV tlvList = new HuaweiTLV()
.put(0x82, software)
.put(0x82,device_information)
.put(0x82,user_license);
.put(0x82, device_information)
.put(0x82, user_license);
this.tlv = new HuaweiTLV()
.put(0x81, tlvList);
}
@ -1776,6 +1815,39 @@ public class DeviceConfig {
}
}
public static class ReverseCapabilities {
public static final int id = 0x3f;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
// Bits like ext capabilities
byte[] capabilities = {(byte) 0xFD, 0x17};
this.tlv = new HuaweiTLV()
.put(0x01, capabilities);
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
}
}
}
// TODO: wear location enum?
public static class Date {

View File

@ -104,6 +104,40 @@ public class FileDownloadService2C {
}
}
public static class FileRequestHash {
public static final int id = 0x02;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, byte fileId) {
super(paramsProvider);
this.serviceId = FileDownloadService2C.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, fileId)
.put(0x02, (byte) 0x01);
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public byte fileId;
public byte[] fileHash;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
}
@Override
public void parseTlv() throws ParseException {
fileId = this.tlv.getByte(0x01);
fileHash = this.tlv.getBytes(0x03);
}
}
}
public static class FileInfo {
public static final int id = 0x03;
@ -207,7 +241,7 @@ public class FileDownloadService2C {
public static class FileDownloadCompleteRequest extends HuaweiPacket {
public static final int id = 0x06;
public FileDownloadCompleteRequest(ParamsProvider paramsProvider, byte fileId) {
public FileDownloadCompleteRequest(ParamsProvider paramsProvider, byte fileId, byte status) {
super(paramsProvider);
this.serviceId = FileDownloadService2C.id;
@ -215,9 +249,72 @@ public class FileDownloadService2C {
this.tlv = new HuaweiTLV()
.put(0x01, fileId)
.put(0x02, (byte) 1);
.put(0x02, status);
this.complete = true;
}
}
public static class IncomingInitRequest {
public static final int id = 0x07;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, String filename, byte fileType, byte fileId, int fileSize, String srcPackage, String dstPackage, String srcFingerprint, String dstFingerprint, byte status) {
super(paramsProvider);
this.serviceId = FileDownloadService2C.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, filename)
.put(0x02, fileType)
.put(0x03, fileId)
.put(0x04, fileSize);
if (srcPackage != null && dstPackage != null) {
this.tlv.put(0x08, srcPackage)
.put(0x09, dstPackage)
.put(0x0a, srcFingerprint)
.put(0x0b, dstFingerprint);
}
this.tlv.put(0x0d, status);
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public String filename;
public byte fileType;
public byte fileId;
public int fileSize;
public String description;
public String srcPackage;
public String dstPackage;
public String srcFingerprint = null;
public String dstFingerprint = null;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
}
@Override
public void parseTlv() throws ParseException {
filename = this.tlv.getString(0x01);
fileType = this.tlv.getByte(0x02);
fileId = this.tlv.getByte(0x03);
fileSize = this.tlv.getAsInteger(0x4);
description = this.tlv.getString(0x07);
srcPackage = this.tlv.getString(0x08);
dstPackage = this.tlv.getString(0x09);
if (this.tlv.contains(0x0a))
srcFingerprint = this.tlv.getString(0x0a);
if (this.tlv.contains(0x0b))
dstFingerprint = this.tlv.getString(0x0b);
}
}
}
}

View File

@ -53,6 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DataSync;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DeviceConfig;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Ephemeris;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.EphemerisFileUpload;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService2C;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FindPhone;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Menstrual;
@ -135,6 +136,7 @@ public class AsynchronousResponse {
handlePermissionCheck(response);
handleDataSyncCommands(response);
handleOTA(response);
handleFileDownload(response);
} catch (Request.ResponseParseException e) {
LOG.error("Response parse exception", e);
@ -831,4 +833,16 @@ public class AsynchronousResponse {
support.getHuaweiOTAManager().handleDeviceError(((OTA.DeviceError.Response) response).errorCode);
}
}
void handleFileDownload(HuaweiPacket response) throws Request.ResponseTypeMismatchException {
if (response.serviceId != FileDownloadService2C.id)
return;
if (response.commandId == FileDownloadService2C.IncomingInitRequest.id) {
if (!(response instanceof FileDownloadService2C.IncomingInitRequest.Response)) {
throw new Request.ResponseTypeMismatchException(response, FileDownloadService2C.IncomingInitRequest.class);
}
FileDownloadService2C.IncomingInitRequest.Response resp = (FileDownloadService2C.IncomingInitRequest.Response) response;
support.deviceFileDownloadRequest(resp.filename, resp.fileType, resp.fileId, resp.fileSize, resp.srcPackage, resp.dstPackage, resp.srcFingerprint, resp.dstFingerprint);
}
}
}

View File

@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
@ -38,6 +39,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadS
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetFileBlockRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetFileDownloadCompleteRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetFileDownloadInitRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetFileHashRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetFileIncomingAck;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetFileInfoRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetFileParametersRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Request;
@ -79,6 +82,12 @@ public class HuaweiFileDownloadManager {
}
}
public static class HuaweiFileDownloadVerifyException extends HuaweiFileDownloadException {
HuaweiFileDownloadVerifyException(@Nullable FileRequest fileRequest) {
super(fileRequest, "Verify failed");
}
}
public static class HuaweiFileDownloadFileMismatchException extends HuaweiFileDownloadException {
HuaweiFileDownloadFileMismatchException(@NonNull FileRequest fileRequest, String filename) {
super(fileRequest, "Data for wrong file received. Expected name " + fileRequest.filename + ", got name " + filename);
@ -96,9 +105,9 @@ public class HuaweiFileDownloadManager {
super(
fileRequest,
"Data for wrong file received. Expected " +
(newSync ?
"id " + fileRequest.fileId + ", got id " + number :
"packet number " + (fileRequest.lastPacketNumber + 1) + ", got " + number)
(newSync ?
"id " + fileRequest.fileId + ", got id " + number :
"packet number " + (fileRequest.lastPacketNumber + 1) + ", got " + number)
);
}
}
@ -112,7 +121,8 @@ public class HuaweiFileDownloadManager {
}
public static class FileDownloadCallback {
public void downloadComplete(FileRequest fileRequest) { }
public void downloadComplete(FileRequest fileRequest) {
}
public void downloadException(HuaweiFileDownloadException e) {
if (e.fileRequest != null)
@ -129,7 +139,7 @@ public class HuaweiFileDownloadManager {
private final FileType fileType;
private final boolean newSync;
FileDownloadCallback fileDownloadCallback = null;
private final FileDownloadCallback fileDownloadCallback;
// Sleep type only - for 2C GPS they are set to zero
private int startTime = 0;
@ -139,6 +149,9 @@ public class HuaweiFileDownloadManager {
private short workoutId;
private Long databaseId;
private boolean initFormDevice = false;
private FileRequest(String filename, FileType fileType, boolean newSync, int startTime, int endTime, FileDownloadCallback fileDownloadCallback) {
this.filename = filename;
this.fileType = fileType;
@ -148,6 +161,10 @@ public class HuaweiFileDownloadManager {
this.endTime = endTime;
}
public static FileRequest IncomingFileRequest(String filename, FileDownloadCallback fileDownloadCallback) {
return new FileRequest(filename, FileType.UNKNOWN, true, fileDownloadCallback);
}
public static FileRequest sleepStateFileRequest(boolean supportsTruSleepNewSync, int startTime, int endTime, FileDownloadCallback fileDownloadCallback) {
return new FileRequest("sleep_state.bin", FileType.SLEEP_STATE, supportsTruSleepNewSync, startTime, endTime, fileDownloadCallback);
}
@ -200,6 +217,15 @@ public class HuaweiFileDownloadManager {
// New sync only
private byte fileId;
private boolean noEncrypt;
private boolean needVerify = false;
private byte[] fileHash = null;
// Incoming P2P request
private byte inFileType;
private String srcPackage = null;
private String dstPackage = null;
private String srcFingerprint = null;
private String dstFingerprint = null;
public byte getFileId() {
return fileId;
@ -215,7 +241,7 @@ public class HuaweiFileDownloadManager {
public byte[] getData() {
if (buffer == null)
return new byte[] {};
return new byte[]{};
return buffer.array();
}
@ -250,6 +276,74 @@ public class HuaweiFileDownloadManager {
public boolean isNoEncrypt() {
return noEncrypt;
}
public boolean isInitFormDevice() {
return initFormDevice;
}
public void setInitFormDevice(boolean initFormDevice) {
this.initFormDevice = initFormDevice;
}
public String getSrcPackage() {
return srcPackage;
}
public void setSrcPackage(String srcPackage) {
this.srcPackage = srcPackage;
}
public String getDstPackage() {
return dstPackage;
}
public void setDstPackage(String dstPackage) {
this.dstPackage = dstPackage;
}
public String getSrcFingerprint() {
return srcFingerprint;
}
public void setSrcFingerprint(String srcFingerprint) {
this.srcFingerprint = srcFingerprint;
}
public String getDstFingerprint() {
return dstFingerprint;
}
public void setDstFingerprint(String dstFingerprint) {
this.dstFingerprint = dstFingerprint;
}
public void setFileId(byte fileId) {
this.fileId = fileId;
}
public int getFileSize() {
return fileSize;
}
public void setFileSize(int fileSize) {
this.fileSize = fileSize;
}
public byte getInFileType() {
return inFileType;
}
public void setInFileType(byte inFileType) {
this.inFileType = inFileType;
}
public boolean isNeedVerify() {
return needVerify;
}
public void setNeedVerify(boolean needVerify) {
this.needVerify = needVerify;
}
}
/**
@ -272,9 +366,9 @@ public class HuaweiFileDownloadManager {
public boolean handleResponse(HuaweiPacket response) {
if (
(response.serviceId == FileDownloadService0A.id &&
response.commandId == FileDownloadService0A.BlockResponse.id) ||
(response.serviceId == FileDownloadService2C.id &&
response.commandId == FileDownloadService2C.BlockResponse.id)
response.commandId == FileDownloadService0A.BlockResponse.id) ||
(response.serviceId == FileDownloadService2C.id &&
response.commandId == FileDownloadService2C.BlockResponse.id)
) {
receivedPacket = response;
return true;
@ -350,6 +444,14 @@ public class HuaweiFileDownloadManager {
if (needSync)
this.needSync = true;
}
if (fileRequest.isInitFormDevice()) {
GetFileIncomingAck getFileIncomingAck = new GetFileIncomingAck(supportProvider, fileRequest, (byte) 0);
try {
getFileIncomingAck.doPerform();
} catch (IOException e) {
LOG.error("Error execute", e);
}
}
startDownload();
}
@ -400,6 +502,11 @@ public class HuaweiFileDownloadManager {
this.currentFileRequest = this.fileRequests.remove(0);
if (this.currentFileRequest.isInitFormDevice()) {
getFileHash();
return;
}
GetFileDownloadInitRequest getFileDownloadInitRequest = new GetFileDownloadInitRequest(supportProvider, currentFileRequest);
getFileDownloadInitRequest.setFinalizeReq(new Request.RequestCallback() {
@Override
@ -514,6 +621,39 @@ public class HuaweiFileDownloadManager {
}
}
private void getFileHash() {
if (!currentFileRequest.isNeedVerify() || !currentFileRequest.isNewSync()) {
getFileInfo();
return;
}
GetFileHashRequest getFileHashRequest = new GetFileHashRequest(supportProvider, currentFileRequest);
getFileHashRequest.setFinalizeReq(new Request.RequestCallback() {
@Override
public void call(Request request) {
GetFileHashRequest r = (GetFileHashRequest) request;
if (currentFileRequest.fileId != r.fileId) {
currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadFileMismatchException(currentFileRequest, r.fileId, true));
reset();
return;
}
currentFileRequest.fileHash = r.fileHash;
getFileInfo();
}
@Override
public void handleException(Request.ResponseParseException e) {
currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadRequestException(null, this.getClass(), e));
reset();
}
});
try {
getFileHashRequest.doPerform();
} catch (IOException e) {
currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileHashRequest, e));
reset();
}
}
private void getFileInfo() {
GetFileInfoRequest getFileInfoRequest = new GetFileInfoRequest(supportProvider, currentFileRequest);
getFileInfoRequest.setFinalizeReq(new Request.RequestCallback() {
@ -624,24 +764,52 @@ public class HuaweiFileDownloadManager {
// Stop timeout from hitting
this.handler.removeCallbacks(this.timeout);
byte status = 1;
if (currentFileRequest.isNeedVerify() && currentFileRequest.isNewSync()) {
status = 2;
try {
if (currentFileRequest.fileHash != null) {
MessageDigest m = MessageDigest.getInstance("SHA256");
m.update(currentFileRequest.getData(), 0, currentFileRequest.fileSize);
byte[] sha256 = m.digest();
LOG.info("SHA256: {} {}", GB.hexdump(sha256), GB.hexdump(currentFileRequest.fileHash));
if (Arrays.equals(sha256, currentFileRequest.fileHash)) {
status = 1;
}
}
} catch (Exception e) {
LOG.error("Error verify SHA256", e);
}
}
// File complete request
GetFileDownloadCompleteRequest getFileDownloadCompleteRequest = new GetFileDownloadCompleteRequest(supportProvider, currentFileRequest);
GetFileDownloadCompleteRequest getFileDownloadCompleteRequest = new GetFileDownloadCompleteRequest(supportProvider, currentFileRequest, status);
try {
getFileDownloadCompleteRequest.doPerform();
} catch (IOException e) {
currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadSendException(currentFileRequest, getFileDownloadCompleteRequest, e));
reset();
return;
}
if (status != 1) {
currentFileRequest.fileDownloadCallback.downloadException(new HuaweiFileDownloadVerifyException(currentFileRequest));
reset();
return;
}
// Handle file data
try {
currentFileRequest.fileDownloadCallback.downloadComplete(currentFileRequest);
} catch (Exception e) {
LOG.error("Download complete callback exception.", e);
LOG.warn("File contents: {}", GB.hexdump(currentFileRequest.getData()));
GB.toast("Workout GPX file could not be parsed.",Toast.LENGTH_SHORT, GB.ERROR, e);
GB.toast("Workout GPX file could not be parsed.", Toast.LENGTH_SHORT, GB.ERROR, e);
}
if (!this.currentFileRequest.newSync && !this.fileRequests.isEmpty() && !this.fileRequests.get(0).newSync) {
// Old sync can potentially take a shortcut
if (arrayContains(this.currentFileRequest.filenames, this.fileRequests.get(0).filename)) {
@ -660,7 +828,6 @@ public class HuaweiFileDownloadManager {
return;
}
}
reset();
}

View File

@ -1,21 +1,37 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
import android.net.Uri;
import android.text.TextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiBaseP2PService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PWakeAppScreenshot;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendP2PCommand;
public class HuaweiP2PManager {
private final Logger LOG = LoggerFactory.getLogger(HuaweiP2PManager.class);
public interface HuaweiWakeApp {
boolean onWakeApp(HuaweiP2PManager manager, Uri uri);
}
private final HuaweiSupportProvider support;
private final List<HuaweiBaseP2PService> registeredServices;
private final Map<String, HuaweiWakeApp> supportedWakeApp;
private Short sequence = 1;
public synchronized Short getNextSequence() {
@ -25,6 +41,8 @@ public class HuaweiP2PManager {
public HuaweiP2PManager(HuaweiSupportProvider support) {
this.support = support;
this.registeredServices = new ArrayList<>();
this.supportedWakeApp = new HashMap<>();
this.supportedWakeApp.put("/router/device/screenshot",new HuaweiP2PWakeAppScreenshot());
}
public HuaweiSupportProvider getSupportProvider() {
@ -59,9 +77,71 @@ public class HuaweiP2PManager {
registeredServices.clear();
}
public void sendAck(short sequence, String srcPackage, String dstPackage, int code) {
try {
SendP2PCommand test = new SendP2PCommand(this.getSupportProvider(), (byte) 3, sequence, srcPackage, dstPackage, null, null, null, code);
test.doPerform();
} catch (IOException e) {
LOG.error("P2P Service error send ACK", e);
}
}
public int handleLink(String link) {
if (TextUtils.isEmpty(link) || (!link.startsWith("huaweischeme://healthapp/router/") && !link.startsWith("huaweischeme://healthapp/home/"))) {
return 0xd2;
}
Uri uri = Uri.parse(link);
LOG.info("Path: {}", uri.getPath());
HuaweiWakeApp svr = supportedWakeApp.get(uri.getPath());
if(svr != null && svr.onWakeApp(this, uri)) {
return 0xd1; //success
}
return 0xd2;
}
public int handleWakeApp(P2P.P2PCommand.Response packet) {
if (packet.respData == null || packet.respData.length == 0) {
return 0xcc;
}
HuaweiTLV tlv = new HuaweiTLV();
tlv.parse(packet.respData);
String link = "";
if(tlv.contains(0x04)) {
try {
link = tlv.getString(0x04);
} catch (HuaweiPacket.MissingTagException e) {
LOG.error("P2P Service error get link", e);
}
}
if (!TextUtils.isEmpty(link)) {
return handleLink(link);
}
// TODO: support other TLV.
return 0xcd;
}
public void handleFile(String srcPackage, String dstPackage, String srcFingerprint, String dstFingerprint, String filename, byte[] data) {
// NOTE: Maybe packet should be found by dstPacket or as a pair package + fingerprint
for (HuaweiBaseP2PService service : registeredServices) {
if (service.getPackage().equals(srcPackage)) {
service.handleFile(filename, data);
}
}
}
public void handlePacket(P2P.P2PCommand.Response packet) {
LOG.info("P2P Service message: Src: {} Dst: {} Seq: {}", packet.srcPackage, packet.dstPackage, packet.sequenceId);
if(packet.cmdId == 1) {
String[] split = packet.dstPackage.split("\\.");
if (split.length > 2 && split[2].equals("wakeapp")) {
int code = handleWakeApp(packet);
sendAck(packet.sequenceId, packet.dstPackage, packet.srcPackage, code);
return;
}
}
for (HuaweiBaseP2PService service : registeredServices) {
if (service.getPackage().equals(packet.srcPackage)) {
service.handlePacket(packet);

View File

@ -130,15 +130,16 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetN
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.GetWorkoutCapability;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendCameraRemoteSetupEvent;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendDeviceReportThreshold;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendExtendedAccountRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFitnessUserInfoRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendGetDefaultSwitch;
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.SendReverseCapabilitiesRequest;
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;
@ -823,13 +824,14 @@ public class HuaweiSupportProvider {
initRequestQueue.add(new SendExtendedAccountRequest(this));
initRequestQueue.add(new GetSettingRelatedRequest(this));
initRequestQueue.add(new AcceptAgreementsRequest(this));
initRequestQueue.add(new SendReverseCapabilitiesRequest(this));
initRequestQueue.add(new SendSetUpDeviceStatusRequest(this));
initRequestQueue.add(new GetActivityTypeRequest(this));
initRequestQueue.add(new GetWearStatusRequest(this));
initRequestQueue.add(new GetConnectStatusRequest(this));
initRequestQueue.add(new GetDndLiftWristTypeRequest(this));
initRequestQueue.add(new SendDndDeleteRequest(this));
initRequestQueue.add(new SendDndAddRequest(this));
initRequestQueue.add(new SendSetUpDeviceStatusRequest(this));
initRequestQueue.add(new GetWearStatusRequest(this));
initRequestQueue.add(new SendMenstrualCapabilityRequest(this));
initRequestQueue.add(new SendNotifyHeartRateCapabilityRequest(this));
initRequestQueue.add(new SendNotifyRestHeartRateCapabilityRequest(this));
@ -844,6 +846,7 @@ public class HuaweiSupportProvider {
initRequestQueue.add(new GetWatchfaceParams(this));
initRequestQueue.add(new SendCameraRemoteSetupEvent(this, CameraRemote.CameraRemoteSetup.Request.Event.ENABLE_CAMERA));
initRequestQueue.add(new GetAppInfoParams(this));
initRequestQueue.add(new SendGetDefaultSwitch(this));
initRequestQueue.add(new GetMusicInfoParams(this));
initRequestQueue.add(new GetExtendedMusicInfoParams(this));
initRequestQueue.add(new SetActivateOnLiftRequest(this));
@ -2506,6 +2509,35 @@ public class HuaweiSupportProvider {
this.huaweiDataSyncTreeCircleGoals = null;
}
public void deviceFileDownloadRequest(String filename, byte fileType, byte fileId, int fileSize, String srcPackage, String dstPackage, String srcFingerprint, String dstFingerprint) {
HuaweiFileDownloadManager.FileRequest request = HuaweiFileDownloadManager.FileRequest.IncomingFileRequest(filename, new HuaweiFileDownloadManager.FileDownloadCallback() {
@Override
public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) {
LOG.info("Download file: {}", fileRequest.getFilename());
huaweiP2PManager.handleFile(fileRequest.getSrcPackage(), fileRequest.getDstPackage(), fileRequest.getSrcFingerprint(), fileRequest.getDstFingerprint(), fileRequest.getFilename(), fileRequest.getData());
}
@Override
public void downloadException(HuaweiFileDownloadManager.HuaweiFileDownloadException e) {
super.downloadException(e);
LOG.debug("Download exception");
}
});
request.setInitFormDevice(true);
request.setInFileType(fileType);
request.setFileId(fileId);
request.setFileSize(fileSize);
request.setSrcPackage(srcPackage);
request.setDstPackage(dstPackage);
request.setSrcFingerprint(srcFingerprint);
request.setDstFingerprint(dstFingerprint);
request.setNeedVerify(true);
huaweiFileDownloadManager.addToQueue(request, false);
}
public boolean downloadTruSleepData(int start, int end) {
// We only get the data if TruSleep is supported
if (!getHuaweiCoordinator().supportsTruSleep())

View File

@ -81,15 +81,6 @@ public abstract class HuaweiBaseP2PService {
}
}
public void sendAck(short sequence, String srcPackage, String dstPackage, int code) {
try {
SendP2PCommand test = new SendP2PCommand(this.manager.getSupportProvider(), (byte) 3, sequence, srcPackage, dstPackage, null, null, null, code);
test.doPerform();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void handlePacket(P2P.P2PCommand.Response packet) {
LOG.info("HuaweiBaseP2PService handlePacket: {} Code: {}", packet.cmdId, packet.respCode);
if (waitPackets.containsKey(packet.sequenceId)) {
@ -101,14 +92,17 @@ public abstract class HuaweiBaseP2PService {
LOG.error("HuaweiBaseP2PService handler is null");
}
} else {
if (packet.cmdId == 1) { //Ping
sendAck(packet.sequenceId, packet.dstPackage, packet.srcPackage, 0xca);
manager.sendAck(packet.sequenceId, packet.dstPackage, packet.srcPackage, 0xcf);
} else if (packet.cmdId == 2) {
manager.sendAck(packet.sequenceId, packet.dstPackage, packet.srcPackage, 0xcf);
handleData(packet.respData);
sendAck(packet.sequenceId, packet.dstPackage, packet.srcPackage, 0xca);
}
}
}
public void handleFile(String filename, byte[] data) {
}
}

View File

@ -0,0 +1,348 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiP2PManager;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
public class HuaweiP2PScreenshotService extends HuaweiBaseP2PService {
private final Logger LOG = LoggerFactory.getLogger(HuaweiP2PScreenshotService.class);
public static final String MODULE = "hw.unitedevice.screenshot";
public static class DeviceInfo {
@SerializedName("bezierCurvePoint")
public BezierCurvePoint bezierCurvePoint;
@SerializedName("cutout")
public int cutout;
@SerializedName("compressionAlgorithm")
public int compressionAlgorithm;
@SerializedName("photoHeight")
public int photoHeight;
@SerializedName("radius")
public float[] radius;
@SerializedName("photoWidth")
public int photoWidth;
@SerializedName("roundedCutType")
public int roundedCutType;
@SerializedName("startPointX")
public int startPointX;
@SerializedName("startPointY")
public int startPointY;
@SerializedName("watchType")
public int watchType;
public static class BezierCurvePoint {
@SerializedName("inPoint")
public float[][] inPoint;
@SerializedName("outPoint")
public float[][] outPoint;
}
}
// Crop related constants.
private final int PIXELS_TO_CROP = 1;
private final int CROP_BORDER_WIDTH = 2;
private final int CROP_BORDER_COLOR = Color.BLACK;
private final int CROP_BACKGROUND_COLOR = Color.WHITE;
private DeviceInfo deviceInfo = null;
public HuaweiP2PScreenshotService(HuaweiP2PManager manager) {
super(manager);
LOG.info("HuaweiP2PScreenshotService");
}
@Override
public String getModule() {
return HuaweiP2PScreenshotService.MODULE;
}
@Override
public String getPackage() {
return "com.huawei.watch.screenshot";
}
@Override
public String getFingerprint() {
return "SystemApp";
}
private String startServiceData() {
JSONObject data = new JSONObject();
try {
data.put("operateType", 1);
data.put("statusCode", 1);
data.put("hasPermission", 1);
} catch (JSONException e) {
LOG.error("startServiceData: Failed to prepare JSOM Object", e);
}
return data.toString();
}
private String createResponse(int i) {
JSONObject data = new JSONObject();
try {
data.put("operateType", 3);
data.put("statusCode", i);
} catch (JSONException e) {
LOG.error("createResponse: Failed to prepare JSOM Object", e);
}
return data.toString();
}
private String createDownloadResponse(String filename, int i) {
JSONObject data = new JSONObject();
try {
data.put("operateType", 2);
data.put("fileName", filename);
data.put("statusCode", i);
} catch (JSONException e) {
LOG.error("createResponse: Failed to prepare JSOM Object", e);
}
return data.toString();
}
public void sendNegotiateConfig() {
String packet = startServiceData();
LOG.info("HuaweiP2PScreenshotService sendNegotiateConfig");
sendCommand(packet.getBytes(StandardCharsets.UTF_8), null);
}
@Override
public void registered() {
}
@Override
public void unregister() {
}
@Override
public void handleData(byte[] data) {
if (data == null) {
LOG.error("HuaweiP2PScreenshotService data is null");
return;
}
String config = new String(data, StandardCharsets.UTF_8);
if (TextUtils.isEmpty(config)) {
LOG.error("HuaweiP2PScreenshotService config is empty");
return;
}
LOG.info("HuaweiP2PScreenshotService handleData: {}", config);
DeviceInfo deviceInfo = null;
try {
deviceInfo = new Gson().fromJson(config, DeviceInfo.class);
} catch (JsonSyntaxException e) {
LOG.error("HuaweiP2PScreenshotService error parse config", e);
}
int status = 2;
if (deviceInfo == null) {
LOG.error("HuaweiP2PScreenshotService deviceInfo is null");
} else {
if (deviceInfo.compressionAlgorithm == 1) {
status = 1;
}
}
if (status == 1) {
this.deviceInfo = deviceInfo;
}
sendCommand(createResponse(status).getBytes(StandardCharsets.UTF_8), null);
}
private int getWightWithoutCropPixels(int width) {
return width - PIXELS_TO_CROP * 2; // crop from both sides.
}
private int getHeightWithoutCropPixels(int height) {
return height - PIXELS_TO_CROP * 2; // crop from both sides.
}
private Bitmap prepareCroppedImageCircle(Bitmap image) {
int cropWidth = getWightWithoutCropPixels(this.deviceInfo.photoWidth);
int cropHeight = getHeightWithoutCropPixels(this.deviceInfo.photoHeight);
int width = getWightWithoutCropPixels(this.deviceInfo.photoWidth) + CROP_BORDER_WIDTH * 2; // we have border on both sides
int height = getHeightWithoutCropPixels(this.deviceInfo.photoHeight) + CROP_BORDER_WIDTH * 2; // we have border on both sides
Bitmap createBitmap = Bitmap.createBitmap(width, height, image.getConfig());
Canvas canvas = new Canvas(createBitmap);
canvas.drawColor(CROP_BACKGROUND_COLOR);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(CROP_BORDER_WIDTH);
paint.setColor(CROP_BORDER_COLOR);
canvas.drawCircle(((float) width) / 2.0f, ((float) height) / 2.0f, ((float) Math.min(width - CROP_BORDER_WIDTH, height - CROP_BORDER_WIDTH)) / 2.0f, paint);
Bitmap croppedImage = cropImageCircle(image, cropWidth, cropHeight);
canvas.drawBitmap(croppedImage, ((float) (width - cropWidth)) / 2.0f, ((float) (height - cropHeight)) / 2.0f, new Paint(Paint.ANTI_ALIAS_FLAG));
return createBitmap;
}
private Bitmap cropImageCircle(Bitmap srcImage, int width, int height) {
Bitmap resultImage = Bitmap.createBitmap(width, height, srcImage.getConfig());
Canvas canvas = new Canvas(resultImage);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(((float) width) / 2.0f, ((float) height) / 2.0f, ((float) Math.min(width, height)) / 2.0f, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
canvas.drawBitmap(srcImage, (float) (this.deviceInfo.startPointX - PIXELS_TO_CROP), (float) (this.deviceInfo.startPointY - PIXELS_TO_CROP), paint);
return resultImage;
}
private Bitmap prepareCroppedImageRounded(Bitmap image) {
//TODO: implement crop for device with rounded corners
return image;
}
private Bitmap prepareCroppedImage(Bitmap bitmap) {
if (bitmap == null)
return null;
if (this.deviceInfo.watchType == 1) {
return prepareCroppedImageCircle(bitmap);
} else if (this.deviceInfo.watchType == 2) {
return prepareCroppedImageRounded(bitmap);
}
LOG.info("HuaweiP2PScreenshotService unknown watchType: {}", this.deviceInfo.watchType);
// I don't know what to do here. So save image as is.
return bitmap;
}
private boolean saveBitmapOld(String filename, Bitmap bitmap) {
LOG.info("HuaweiP2PScreenshotService saveBitmapOld: {}", filename);
File targetFile;
try {
targetFile = new File(FileUtils.getExternalFilesDir() + File.separator + filename);
} catch (IOException e) {
LOG.error("Could not open Screenshot file to write to", e);
return false;
}
try {
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)) {
fileOutputStream.close();
return false;
}
fileOutputStream.close();
} catch (IOException e) {
LOG.error("Error save bitmap", e);
return false;
}
return true;
}
private boolean saveMediaStore(String filename, Bitmap bitmap) {
LOG.info("HuaweiP2PScreenshotService saveMediaStore: {}", filename);
String relativePath = Environment.DIRECTORY_PICTURES + File.separator + "Screenshots";
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativePath);
contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, filename);
contentValues.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
contentValues.put(MediaStore.Images.ImageColumns.IS_PENDING, 1);
ContentResolver contentResolver = GBApplication.getContext().getContentResolver();
Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
if (uri == null) {
contentValues.clear();
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
return false;
}
try {
OutputStream stream = contentResolver.openOutputStream(uri);
if (stream != null && bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)) {
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
contentResolver.update(uri, contentValues, null, null);
stream.close();
return true;
}
if (stream != null) {
stream.close();
}
} catch (Exception e) {
LOG.error("Error save screenshot", e);
}
contentValues.clear();
contentValues.put(MediaStore.Images.ImageColumns.IS_PENDING, 0);
contentResolver.update(uri, contentValues, null, null);
return false;
}
private boolean saveBitmap(String filename, Bitmap bitmap) {
if (Build.VERSION.SDK_INT < 29)
return saveBitmapOld(filename, bitmap);
return saveMediaStore(filename, bitmap);
}
@Override
public void handleFile(String filename, byte[] data) {
LOG.info("HuaweiP2PScreenshotService handleFile: {}", filename);
if (data == null) {
LOG.error("HuaweiP2PScreenshotService empty file data");
sendCommand(createDownloadResponse(filename, 2).getBytes(StandardCharsets.UTF_8), null);
return;
}
if (this.deviceInfo == null) {
LOG.error("HuaweiP2PScreenshotService no device config");
sendCommand(createDownloadResponse(filename, 2).getBytes(StandardCharsets.UTF_8), null);
return;
}
Bitmap image = null;
if (this.deviceInfo.compressionAlgorithm == 1) {
image = BitmapFactory.decodeByteArray(data, 0, data.length);
}
if (this.deviceInfo.cutout == 1) {
image = this.prepareCroppedImage(image);
}
if (image == null) {
LOG.error("HuaweiP2PScreenshotService no image");
sendCommand(createDownloadResponse(filename, 2).getBytes(StandardCharsets.UTF_8), null);
return;
}
if (saveBitmap(filename, image)) {
sendCommand(createDownloadResponse(filename, 1).getBytes(StandardCharsets.UTF_8), null);
} else {
LOG.error("HuaweiP2PScreenshotService error to save image");
sendCommand(createDownloadResponse(filename, 2).getBytes(StandardCharsets.UTF_8), null);
}
}
public static HuaweiP2PScreenshotService getRegisteredInstance(HuaweiP2PManager manager) {
return (HuaweiP2PScreenshotService) manager.getRegisteredService(HuaweiP2PScreenshotService.MODULE);
}
}

View File

@ -0,0 +1,20 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p;
import android.net.Uri;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiP2PManager;
public class HuaweiP2PWakeAppScreenshot implements HuaweiP2PManager.HuaweiWakeApp {
@Override
public boolean onWakeApp(HuaweiP2PManager manager, Uri uri) {
if (HuaweiP2PScreenshotService.getRegisteredInstance(manager) == null) {
new HuaweiP2PScreenshotService(manager).register();
}
HuaweiP2PScreenshotService screenshotService = HuaweiP2PScreenshotService.getRegisteredInstance(manager);
screenshotService.sendNegotiateConfig();
return true;
}
}

View File

@ -27,8 +27,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupport
public class GetFileDownloadCompleteRequest extends Request {
private final HuaweiFileDownloadManager.FileRequest request;
private final byte status;
public GetFileDownloadCompleteRequest(HuaweiSupportProvider support, HuaweiFileDownloadManager.FileRequest request) {
public GetFileDownloadCompleteRequest(HuaweiSupportProvider support, HuaweiFileDownloadManager.FileRequest request, byte status) {
super(support);
if (request.isNewSync()) {
this.serviceId = FileDownloadService2C.id;
@ -38,13 +39,14 @@ public class GetFileDownloadCompleteRequest extends Request {
this.commandId = FileDownloadService0A.FileDownloadCompleteRequest.id;
}
this.request = request;
this.status = status;
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
try {
if (request.isNewSync())
return new FileDownloadService2C.FileDownloadCompleteRequest(paramsProvider, this.request.getFileId()).serialize();
return new FileDownloadService2C.FileDownloadCompleteRequest(paramsProvider, this.request.getFileId(), status).serialize();
else
return new FileDownloadService0A.FileDownloadCompleteRequest(paramsProvider).serialize();
} catch (HuaweiPacket.CryptoException e) {

View File

@ -0,0 +1,45 @@
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.FileDownloadService2C;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiFileDownloadManager;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class GetFileHashRequest extends Request {
private final HuaweiFileDownloadManager.FileRequest request;
public boolean newSync;
public byte fileId;
public byte[] fileHash;
public GetFileHashRequest(HuaweiSupportProvider support, HuaweiFileDownloadManager.FileRequest request) {
super(support);
this.serviceId = FileDownloadService2C.id;
this.commandId = FileDownloadService2C.FileRequestHash.id;
this.request = request;
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
try {
return new FileDownloadService2C.FileRequestHash.Request(paramsProvider, this.request.getFileId()).serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e);
}
}
@Override
protected void processResponse() throws ResponseParseException {
if (this.receivedPacket instanceof FileDownloadService2C.FileRequestHash.Response) {
this.newSync = true;
FileDownloadService2C.FileRequestHash.Response packet = (FileDownloadService2C.FileRequestHash.Response) this.receivedPacket;
this.fileId = packet.fileId;
this.fileHash = packet.fileHash;
} else {
throw new ResponseTypeMismatchException(this.receivedPacket, FileDownloadService2C.FileInfo.Response.class);
}
}
}

View File

@ -0,0 +1,32 @@
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.FileDownloadService2C;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiFileDownloadManager;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class GetFileIncomingAck extends Request {
private final HuaweiFileDownloadManager.FileRequest request;
private final byte status;
public GetFileIncomingAck(HuaweiSupportProvider support, HuaweiFileDownloadManager.FileRequest request, byte status) {
super(support);
this.serviceId = FileDownloadService2C.id;
this.commandId = FileDownloadService2C.IncomingInitRequest.id;
this.request = request;
this.status = status;
this.addToResponse = false;
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
try {
return new FileDownloadService2C.IncomingInitRequest.Request(this.paramsProvider, this.request.getFilename(), this.request.getInFileType(), this.request.getFileId(), this.request.getFileSize(), this.request.getSrcPackage(), this.request.getDstPackage(), this.request.getSrcFingerprint(), this.request.getDstFingerprint(), this.status).serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e);
}
}
}

View File

@ -54,7 +54,7 @@ public class GetSupportedCommandsRequest extends Request {
DeviceConfig.SupportedCommands.Request commandsRequest = new DeviceConfig.SupportedCommands.Request(paramsProvider);
byte nextService = activatedServices.remove(0);
boolean fits = commandsRequest.addCommandsForService(nextService, this.commandsPerService.get((int) nextService));
while (fits && activatedServices.size() > 0) {
while (fits && !activatedServices.isEmpty()) {
nextService = activatedServices.remove(0);
fits = commandsRequest.addCommandsForService(nextService, this.commandsPerService.get((int) nextService));
}
@ -88,18 +88,12 @@ public class GetSupportedCommandsRequest extends Request {
);
}
if (activatedServices.size() > 0) {
if (!activatedServices.isEmpty()) {
GetSupportedCommandsRequest nextRequest = new GetSupportedCommandsRequest(supportProvider, activatedServices);
this.nextRequest(nextRequest);
} else {
supportProvider.getHuaweiCoordinator().printCommandsPerService();
if (supportProvider.getHuaweiCoordinator().supportsExpandCapability()) {
GetExpandCapabilityRequest nextRequest = new GetExpandCapabilityRequest(supportProvider);
nextRequest.setFinalizeReq(dynamicServicesReq);
this.nextRequest(nextRequest);
} else {
dynamicServicesReq.call();
}
dynamicServicesReq.call();
}
}
}

View File

@ -56,6 +56,7 @@ public class GetSupportedServicesRequest extends Request {
byte[] supportedServices = ((DeviceConfig.SupportedServices.Response) receivedPacket).supportedServices;
List<Byte> activatedServices = new ArrayList<>();
activatedServices.add((byte) 0x01); // device always support 1 service. add it
for (int i = 0; i < supportedServices.length; i++) {
if (supportedServices[i] == 1) {
activatedServices.add(knownSupportedServices[i]);

View File

@ -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.DeviceConfig;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class SendGetDefaultSwitch extends Request {
private static final Logger LOG = LoggerFactory.getLogger(SendGetDefaultSwitch.class);
public SendGetDefaultSwitch(HuaweiSupportProvider support) {
super(support);
this.serviceId = DeviceConfig.id;
this.commandId = DeviceConfig.GetDefaultSwitch.id;
}
@Override
protected boolean requestSupported() {
return supportProvider.getHuaweiCoordinator().supportDefaultSwitch();
}
@Override
protected List<byte[]> createRequest() throws Request.RequestCreationException {
try {
return new DeviceConfig.GetDefaultSwitch.Request(paramsProvider).serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new Request.RequestCreationException(e);
}
}
@Override
protected void processResponse() throws Request.ResponseParseException {
LOG.debug("handle GetDefaultSwitch");
}
}

View File

@ -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.DeviceConfig;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class SendReverseCapabilitiesRequest extends Request {
private static final Logger LOG = LoggerFactory.getLogger(SendReverseCapabilitiesRequest.class);
public SendReverseCapabilitiesRequest(HuaweiSupportProvider support) {
super(support);
this.serviceId = DeviceConfig.id;
this.commandId = DeviceConfig.ReverseCapabilities.id;
}
@Override
protected boolean requestSupported() {
return supportProvider.getHuaweiCoordinator().supportsReverseCapabilities();
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
DeviceConfig.ReverseCapabilities.Request reverseCapabilitiesRequest = new DeviceConfig.ReverseCapabilities.Request(paramsProvider);
try {
return reverseCapabilitiesRequest.serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e);
}
}
@Override
protected void processResponse() throws ResponseParseException {
LOG.debug("handle ReverseCapabilities");
}
}

View File

@ -27,7 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DeviceConfig.
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class SendSetUpDeviceStatusRequest extends Request {
private static final Logger LOG = LoggerFactory.getLogger(SetUpDeviceStatusRequest.class);
private static final Logger LOG = LoggerFactory.getLogger(SendSetUpDeviceStatusRequest.class);
public SendSetUpDeviceStatusRequest(HuaweiSupportProvider support) {
super(support);