1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-02 03:16:07 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java
2024-01-07 23:18:08 +01:00

1711 lines
62 KiB
Java

/* Copyright (C) 2021-2023 Gaignon Damien
Copyright (C) 2022-2023 MartinJM
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.devices.huawei.packets;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.json.JSONException;
import org.json.JSONObject;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiUtil;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupplier.HuaweiDeviceType;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV.TLV;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
// TODO: complete responses
public class DeviceConfig {
public static final byte id = 0x01;
public static class LinkParams {
public static final byte id = 0x01;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, HuaweiDeviceType deviceType) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01)
.put(0x02)
.put(0x03)
.put(0x04);
if (deviceType == HuaweiDeviceType.AW) {
this.tlv.put(0x06);
}
this.complete = true;
this.isEncrypted = false;
}
}
public static class Response extends HuaweiPacket {
public byte protocolVersion = 0x00;
public short mtu = 0x0014;
public short sliceSize = 0x00f4;
public byte authVersion = 0x00;
public byte[] serverNonce = new byte[16];
public byte authMode = 0x00;
public byte authAlgo = 0x00;
public byte bondState = 0x00;
public short interval = 0x0;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.isEncrypted = false;
}
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01))
this.protocolVersion = this.tlv.getByte(0x01);
if (this.tlv.contains(0x02))
this.sliceSize = this.tlv.getShort(0x02);
if (this.tlv.contains(0x03))
this.mtu = this.tlv.getShort(0x03);
if (this.tlv.contains(0x04))
this.interval = this.tlv.getShort(0x04);
if (this.tlv.contains(0x05)) {
System.arraycopy(this.tlv.getBytes(0x05), 2, this.serverNonce, 0, 16);
this.authVersion = (byte)this.tlv.getBytes(0x05)[1];
} else
throw new MissingTagException(0x05);
if (this.tlv.contains(0x07))
this.authMode = this.tlv.getByte(0x07);
if (this.tlv.contains(0x08))
this.authAlgo = this.tlv.getByte(0x08);
if (this.tlv.contains(0x09))
this.bondState = this.tlv.getByte(0x09);
}
}
}
public static class SupportedServices {
public static final byte id = 0x02;
// 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
};
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, byte[] allSupportedServices) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, allSupportedServices);
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public byte[] supportedServices;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
}
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x02)) {
this.supportedServices = this.tlv.getBytes(0x02);
} else {
throw new MissingTagException(0x02);
}
}
}
public static class OutgoingRequest extends HuaweiPacket {
public byte[] allSupportedServices = null;
public OutgoingRequest(ParamsProvider paramsProvider) {
super(paramsProvider);
this.complete = false;
}
@Override
public List<byte[]> serialize() throws CryptoException {
return null;
}
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01))
this.allSupportedServices = this.tlv.getBytes(0x01);
else
throw new MissingTagException(0x01);
}
}
}
public static class SupportedCommands {
public static final byte id = 0x03;
public static final TreeMap<Integer, byte[]> commandsPerService = new TreeMap<Integer, byte[]>() {{
put(0x01, new byte[] {0x04, 0x07, 0x08, 0x09, 0x0A, 0x0D, 0x0E, 0x10, 0x11, 0x12, 0x13, 0x14, 0x1B, 0x1A, 0x1D, 0x21, 0x22, 0x23, 0x24, 0x29, 0x2A, 0x2B, 0x32, 0x2E, 0x31, 0x30, 0x35, 0x36, 0x37, 0x2F});
put(0x02, new byte[] {0x01, 0x04, 0x05, 0x06, 0x07, 0x08});
put(0x03, new byte[] {0x01, 0x03, 0x04});
put(0x04, new byte[] {0x01});
put(0x05, new byte[] {0x01});
put(0x06, new byte[] {0x01});
put(0x07, new byte[] {0x01, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0A, 0x0E, 0x10, 0x13, 0x16, 0x15, 0x17, 0x18, 0x1B, 0x1C, 0x1D, 0x1E, 0x21, 0x22, 0x23, 0x24, 0x25, 0x28, 0x29, 0x06, 0x1F});
put(0x08, new byte[] {0x01, 0x02, 0x03});
put(0x09, new byte[] {0x01, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F});
put(0x0A, new byte[] {0x01, 0x09, 0x0A});
put(0x0B, new byte[] {0x01, 0x03});
put(0x0C, new byte[] {0x01});
put(0x0D, new byte[] {0x01});
put(0x0F, new byte[] {0x01, 0x03, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C});
put(0x10, new byte[] {0x01});
put(0x11, new byte[] {0x01});
put(0x12, new byte[] {0x01});
put(0x13, new byte[] {0x01});
put(0x14, new byte[] {0x01});
put(0x15, new byte[] {0x01});
put(0x16, new byte[] {0x01, 0x03, 0x07});
put(0x17, new byte[] {0x01, 0x04, 0x06, 0x07, 0x0B, 0x0C, 0x10, 0x12, 0x15, 0x17});
put(0x18, new byte[] {0x01, 0x02, 0x04, 0x05, 0x06, 0x09});
put(0x19, new byte[] {0x01, 0x04});
put(0x1A, new byte[] {0x01, 0x03, 0x07, 0x05, 0x06});
put(0x1B, new byte[] {0x01, 0x0F, 0x19, 0x1A});
// No 0x1C
put(0x1D, new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A});
// No 0x1E
// No 0x1F
put(0x20, new byte[] {0x01, 0x02, 0x03, 0x04, 0x09, 0x0A});
//put(0x21, new byte[] {0x01});
put(0x22, new byte[] {0x01});
put(0x23, new byte[] {0x02, 0x0B});
put(0x24, new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C});
put(0x25, new byte[] {0x02, 0x04, 0x0E});
put(0x26, new byte[] {0x02, 0x03});
put(0x27, new byte[] {0x01, 0x0E});
// No 0x28
// No 0x29
put(0x2A, new byte[] {0x01, 0x06});
put(0x2B, new byte[] {0x12});
// No 0x2C
put(0x2D, new byte[] {0x01});
put(0x2E, new byte[] {0x01, 0x02, 0x03});
// No 0x2F
put(0x30, new byte[] {0x01});
// No 0x31
put(0x32, new byte[] {0x01});
put(0x33, new byte[] {0x01, 0x2});
put(0x34, new byte[] {0x01});
put(0x35, new byte[] {0x03, 0x04});
}};
public static class Request extends HuaweiPacket {
private int maxSize;
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV();
// slice size is the max size
// But there is a 4 byte header,
// a two byte body header,
// a two byte footer,
// three bytes get added for the 0x81 tag,
// and 21 for the encryption stuff
this.maxSize = this.paramsProvider.getSliceSize() - 32;
// Now it gets aligned to 16 byte blocks
// And it gets a header and two length bytes
// For CBC there is also at least one byte padding
this.maxSize = this.maxSize - (this.maxSize % 16) - 4;
}
public boolean addCommandsForService(byte service, byte[] commands) {
// tlv length is what we have already
// commands is what we'll add
// together with a 3-byte 0x02 tag and two bytes for length
if (this.tlv.length() + commands.length + 5 > this.maxSize) { //this.paramsProvider.mtu - 5 - 2 - 2 - 2 - 21) {
return false;
}
this.tlv.put(0x02, service).put(0x03, commands);
return true;
}
@Override
public List<byte[]> serialize() throws CryptoException {
this.tlv = new HuaweiTLV()
.put(0x81, this.tlv);
this.complete = true;
return super.serialize();
}
}
public static class Response extends HuaweiPacket {
public static class CommandsList {
public int service;
public byte[] commands;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("CommandsList{service=");
sb.append(Integer.toHexString(service));
sb.append(", commands=[");
for (byte b : commands) {
sb.append(Integer.toHexString(b));
if (b != commands[commands.length - 1]) // Elements should be unique
sb.append(", ");
}
sb.append("]}");
return sb.toString();
}
}
public List<CommandsList> commandsLists;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.isEncrypted = false;
}
@Override
public void parseTlv() throws ParseException {
this.commandsLists = new ArrayList<>();
CommandsList commandsList = null;
HuaweiTLV containerTLV = this.tlv.getObject(0x81);
if (!containerTLV.contains(0x02)) {
throw new MissingTagException(0x02);
}
if (!containerTLV.contains(0x04)) {
throw new MissingTagException(0x04);
}
for (HuaweiTLV.TLV tlv : containerTLV.get()) {
if ((int) tlv.getTag() == 0x02) {
commandsList = new CommandsList();
commandsList.service = ByteBuffer.wrap(tlv.getValue()).get();
} else if ((int) tlv.getTag() == 0x04) {
if (commandsList == null)
throw new SupportedCommandsListException("Commandslist is not yet set");
ByteBuffer buffer = ByteBuffer.allocate(tlv.getValue().length);
for (int i = 0; i < tlv.getValue().length; i++) {
if ((int) tlv.getValue()[i] == 1)
buffer.put((byte) (commandsPerService.get(commandsList.service)[i]));
}
commandsList.commands = new byte[buffer.position()];
((ByteBuffer) buffer.rewind()).get(commandsList.commands);
this.commandsLists.add(commandsList);
} else
throw new SupportedCommandsListException("Unknown tag encountered");
}
}
}
// TODO: LogRequest
}
public static class DateFormat {
public static final byte id = 0x04;
public static class Request extends HuaweiPacket {
public Request(
ParamsProvider paramsProvider,
byte dateFormat,
byte timeFormat
) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x81, new HuaweiTLV()
.put(0x02, dateFormat)
.put(0x03, timeFormat)
);
this.complete = true;
}
}
public static class OutgoingRequest extends HuaweiPacket {
public byte dateFormat;
public byte timeFormat;
public OutgoingRequest(ParamsProvider paramsProvider) {
super(paramsProvider);
this.complete = false;
}
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x02))
this.dateFormat = this.tlv.getByte(0x02);
if (this.tlv.contains(0x03))
this.timeFormat = this.tlv.getByte(0x03);
}
}
}
public static class TimeRequest extends HuaweiPacket {
public static final byte id = 0x05;
public TimeRequest(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
ByteBuffer timeAndZoneId = ByteBuffer.wrap(HuaweiUtil.getTimeAndZoneId());
this.tlv = new HuaweiTLV()
.put(0x01, timeAndZoneId.getInt(0))
.put(0x02, timeAndZoneId.getShort(4));
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class ProductInfo {
public static final byte id = 0x07;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, HuaweiDeviceType deviceType) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV();
if (deviceType == HuaweiDeviceType.AW) {
int[] tags = {0x01, 0x02, 0x03, 0x07, 0x09, 0x0A, 0x0c, 0x11};
for (int i: tags) {
this.tlv.put(i);
}
} else {
int[] tags = {0x01, 0x02, 0x07, 0x09, 0x0A, 0x11, 0x12, 0x16, 0x1A, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23};
for (int i : tags) {
this.tlv.put(i);
}
} // else if (AW Compatible)
// this.tlv).put(0x01).put(0x02).put(0x07).put(0x09);
// }
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
// TODO: extend:
// public static final int BTVersion = 0x01;
// public static final int productType = 0x02;
// public static final int phoneNumber = 0x04;
// public static final int macAddress = 0x05;
// public static final int IMEI = 0x06;
// public static final int openSourceVersion = 0x08;
// public static final int serialNumber = 0x09;
// public static final int eMMCId = 0x0B;
// public static final int healthAppSupport = 0x0D;
public String hardwareVersion;
public String softwareVersion;
public String productModel;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.isEncrypted = false;
}
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x07))
throw new MissingTagException(0x07);
if (!this.tlv.contains(0x0A))
throw new MissingTagException(0x0A);
if (this.tlv.contains(0x03))
this.hardwareVersion = this.tlv.getString(0x03);
this.softwareVersion = this.tlv.getString(0x07);
this.productModel = this.tlv.getString(0x0A).trim();
}
}
// TODO: implement parsing this request for the log parser support
}
public static class Bond {
public static final byte id = 0x0E;
public static class Request extends HuaweiPacket {
public Request(
ParamsProvider paramsProvider,
byte[] clientSerial,
String mac,
HuaweiCrypto huaweiCrypto
) throws CryptoException {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
byte[] iv = paramsProvider.getIv();
try {
this.tlv = new HuaweiTLV()
.put(0x01)
.put(0x03, (byte) 0x00)
.put(0x05, clientSerial)
.put(0x06, huaweiCrypto.encryptBondingKey(paramsProvider.getSecretKey(), mac, iv))
.put(0x07, iv);
this.isEncrypted = false;
this.complete = true;
} catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | BadPaddingException | InvalidKeyException e) {
throw new CryptoException("Bonding key creation exception", e);
}
}
}
public static class OutgoingRequest extends HuaweiPacket {
public byte[] clientSerial;
public byte[] bondingKey;
public byte[] iv;
public OutgoingRequest(ParamsProvider paramsProvider) {
super(paramsProvider);
this.complete = false;
}
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x05))
this.clientSerial = this.tlv.getBytes(0x05);
else
throw new MissingTagException(0x05);
if (this.tlv.contains(0x06))
this.bondingKey = this.tlv.getBytes(0x06);
else
throw new MissingTagException(0x06);
if (this.tlv.contains(0x07))
this.iv = this.tlv.getBytes(0x07);
else
throw new MissingTagException(0x07);
}
}
}
public static class BondParams {
public static final byte id = 0x0F;
public static class Request extends HuaweiPacket {
public Request(
ParamsProvider paramsProvider,
byte[] clientSerial,
byte[] mac
) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01)
.put(0x03, clientSerial)
.put(0x04, (byte) 0x02)
.put(0x05)
.put(0x07, mac)
.put(0x09);
this.isEncrypted = false;
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public byte status;
public long encryptionCounter;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.isEncrypted = false;
}
@Override
public void parseTlv() {
this.status = this.tlv.getByte(0x01);
this.encryptionCounter = this.tlv.getInteger(0x09) & 0xFFFFFFFFL;
}
}
// TODO: implement parsing this request for the log parser support
}
public static class ActivityType {
public static final int id = 0x12;
public static class Request extends HuaweiPacket {
public Request(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);
this.serviceId = DeviceConfig.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
}
}
}
public static class Auth {
public static final byte id = 0x13;
public static class Request extends HuaweiPacket {
public Request(
ParamsProvider paramsProvider,
byte[] challenge,
byte[] nonce,
boolean isHiChainLite
) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, challenge)
.put(0x02, nonce);
if (isHiChainLite)
this.tlv.put(0x03, (byte)0x02); // Force type 2
this.isEncrypted = false;
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public byte[] challengeResponse;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.isEncrypted = false;
}
@Override
public void parseTlv() {
this.challengeResponse = this.tlv.getBytes(0x01);
}
}
// TODO: implement parsing this request for the log parser support
}
public static class BatteryLevel {
public static final byte id = 0x08;
public static class Request extends HuaweiPacket {
public Request(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 byte level;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
// This differs per watch, so we handle it ourselves in parseTlv
this.isEncrypted = false;
}
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01))
this.level = this.tlv.getByte(0x01);
else
throw new MissingTagException(0x01);
}
}
// TODO: implement parsing this request for the log parser support
}
public static class ActivateOnLiftRequest extends HuaweiPacket {
public static final byte id = 0x09;
public ActivateOnLiftRequest(ParamsProvider paramsProvider, boolean activate) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, activate);
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class DndDeleteRequest extends HuaweiPacket {
public static final int id = 0x0B;
public DndDeleteRequest(HuaweiPacket.ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x81, new HuaweiTLV()
.put(0x02, (byte) 0x01)
);
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class DndAddRequest extends HuaweiPacket {
public static final int id = 0x0C;
public DndAddRequest(
HuaweiPacket.ParamsProvider paramsProvider,
boolean dndEnable,
byte[] start,
byte[] end,
int cycle,
int dndLiftWristType,
boolean allowDndLiftWrist
) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
HuaweiTLV dndPacket = new HuaweiTLV()
.put(0x02, (byte) 0x01)
.put(0x03, dndEnable)
.put(0x04, (byte) 0x00)
.put(0x05, start)
.put(0x06, end)
.put(0x07, (byte) cycle);
if (allowDndLiftWrist) {
dndPacket.put(0x08, (short) (dndLiftWristType));
}
this.tlv = new HuaweiTLV()
.put(0x81, dndPacket);
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class FactoryResetRequest extends HuaweiPacket {
public static final byte id = 0x0D;
public FactoryResetRequest(HuaweiPacket.ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, (byte) 0x01);
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class PhoneInfo {
public static final byte id = 0x10;
public static class Request extends HuaweiPacket {
public Request(HuaweiPacket.ParamsProvider paramsProvider, byte[] phoneInfo) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV();
for (byte b : phoneInfo) {
switch (b) {
case 0xf:
break;
case 0x11:
this.tlv.put((int)b, "1200107310"); // Force AppVersion to 12.1.7.310
break;
case 0x15:
this.tlv.put((int)b, ""); // Force buildOSPlatformVersion to ""
break;
case 0x10: // Force EmuiBuildVersion to 0x00
case 0x13: // Force buildOsEnable to 0x00
case 0x14: // Force buildOSApiVersion to 0x00
default:
this.tlv.put((int)b, "00");
}
}
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public byte[] info;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
}
@Override
public void parseTlv() {
info = new byte[this.tlv.length()];
int i = 0;
for (TLV tlv : this.tlv.get()) {
info[i] = tlv.getTag();
i += 1;
}
}
}
// TODO: implement parsing this request for the log parser support
}
public static class DeviceStatus {
public static final byte id = 0x16;
public static class Request extends HuaweiPacket {
public Request(HuaweiPacket.ParamsProvider paramsProvider, boolean askStatus) { // status or notify
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV();
if (askStatus) {
this.tlv.put(0x01);
} else {
this.tlv.put(0x02, (byte)0x00);
}
this.isEncrypted = false;
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public byte status = -1;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.isEncrypted = false;
}
@Override
public void parseTlv() {
// AW70 doesn't seem to have this
if (this.tlv.contains(0x01))
this.status = this.tlv.getByte(0x01);
}
}
// TODO: implement parsing this request for the log parser support
}
public static class NavigateOnRotateRequest extends HuaweiPacket {
public static final byte id = 0x1B;
public NavigateOnRotateRequest(HuaweiPacket.ParamsProvider paramsProvider, boolean navigate) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, navigate);
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class WearLocationRequest extends HuaweiPacket {
public static final byte id = 0x1A;
public WearLocationRequest(HuaweiPacket.ParamsProvider paramsProvider, byte location) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, location);
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class DndLiftWristType {
public static final int id = 0x1D;
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 int dndLiftWristType;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
}
@Override
public void parseTlv() {
this.dndLiftWristType = (int) this.tlv.getShort(0x01);
}
}
// TODO: implement parsing this request for the log parser support
}
public static class HiChain {
public static final int id = 0x28;
public static class Request {
private final int operationCode;
private final long requestId;
private final byte[] selfAuthId;
private final String groupId;
private JSONObject version = null;
private JSONObject payload = null;
private JSONObject value = null;
public Request (int operationCode, long requestId, byte[] selfAuthId, String groupId) {
this.operationCode = operationCode;
this.requestId = requestId;
this.selfAuthId = selfAuthId;
this.groupId = groupId;
}
public class BaseStep extends HuaweiPacket {
public BaseStep (HuaweiPacket.ParamsProvider paramsProvider, int messageId) throws SerializeException {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = HiChain.id;
this.isSliced = true;
this.isEncrypted = false;
this.complete = true;
version = new JSONObject();
payload = new JSONObject();
value = new JSONObject();
createJson(messageId);
}
}
public class StepOne extends BaseStep {
public StepOne (
HuaweiPacket.ParamsProvider paramsProvider,
int messageId,
byte[] isoSalt,
byte[] seed
) throws SerializeException {
super(paramsProvider, messageId);
// createJson(1); //messageId);
try {
payload
.put("isoSalt", StringUtils.bytesToHex(isoSalt))
.put("peerAuthId", StringUtils.bytesToHex(selfAuthId))
.put("operationCode", operationCode)
.put("seed", StringUtils.bytesToHex(seed))
.put("peerUserType", 0x00);
if (operationCode == 0x02) {
payload
.put("pkgName", "com.huawei.devicegroupmanage")
.put("serviceType", groupId)
.put("keyLength", 0x20);
value.put("isDeviceLevel", false);
}
this.tlv = new HuaweiTLV()
.put(0x01, value.toString())
.put(0x02, (byte)operationCode)
.put(0x03, ByteBuffer.allocate(8).putLong(requestId).array());
//.put(0x04, 0x00)
//.put(0x05, 0x00);
} catch (JSONException e) {
throw new SerializeException("HiChain Step1 JSON exception", e);
}
}
}
public class StepTwo extends BaseStep {
public StepTwo (
HuaweiPacket.ParamsProvider paramsProvider,
int messageId,
byte[] token
) throws SerializeException {
super(paramsProvider, messageId);
// createJson(2); //messageId);
try {
payload
.put("peerAuthId", StringUtils.bytesToHex(selfAuthId))
.put("token", StringUtils.bytesToHex(token));
if (operationCode == 0x02) value.put("isDeviceLevel", false);
this.tlv = new HuaweiTLV()
.put(0x01, value.toString())
.put(0x02, (byte)operationCode)
.put(0x03, ByteBuffer.allocate(8).putLong(requestId).array());
} catch (JSONException e) {
throw new SerializeException("HiChain Step 2 JSON exception", e);
}
}
}
public class StepThree extends BaseStep {
public StepThree (
HuaweiPacket.ParamsProvider paramsProvider,
int messageId,
byte[] nonce,
byte[] encData
) throws SerializeException {
super(paramsProvider, messageId);
// createJson(3);
try {
payload
.put("nonce", StringUtils.bytesToHex(nonce))
.put("encData", StringUtils.bytesToHex(encData));
this.tlv = new HuaweiTLV()
.put(0x01, value.toString())
.put(0x02, (byte)operationCode)
.put(0x03, ByteBuffer.allocate(8).putLong(requestId).array());
} catch (JSONException e) {
throw new SerializeException("HiChain Step 3 JSON exception", e);
}
}
}
public class StepFour extends BaseStep {
public StepFour (
HuaweiPacket.ParamsProvider paramsProvider,
int messageId,
byte[] nonce,
byte[] encResult
) throws SerializeException {
super(paramsProvider, messageId);
// if (operationCode == 0x01) {
// createJson(4); //messageId);
// } else {
// createJson(3);
// }
try {
payload
.put("nonce", StringUtils.bytesToHex(nonce)) //generateRandom
.put("encResult", StringUtils.bytesToHex(encResult))
.put("operationCode", operationCode);
this.tlv = new HuaweiTLV()
.put(0x01, value.toString())
.put(0x02, (byte)operationCode)
.put(0x03, ByteBuffer.allocate(8).putLong(requestId).array());
} catch (JSONException e) {
throw new SerializeException("HiChain Step 4 JSON exception", e);
}
}
}
private void createJson(int messageId) throws HuaweiPacket.SerializeException {
if (operationCode == 0x02) {
messageId |= 0x10;
}
try {
version
.put("minVersion", "1.0.0")
.put("currentVersion", "2.0.16");
payload
.put("version", version);
value
.put("authForm", 0x00)
.put("payload", payload)
.put("groupAndModuleVersion", "2.0.1")
.put("message", messageId);
if (operationCode == 0x01) {
value
.put("requestId", Long.toString(requestId))
.put("groupId", groupId)
.put("groupName", "health_group_name")
.put("groupOp", 2)
.put("groupType", 256)
.put("peerDeviceId", new String(selfAuthId, StandardCharsets.UTF_8))
.put("connDeviceId", new String(selfAuthId, StandardCharsets.UTF_8))
.put("appId", "com.huawei.health")
.put("ownerName", "");
}
} catch (JSONException e) {
throw new HuaweiPacket.SerializeException("Create json Exception", e);
}
}
}
public static class Response extends HuaweiPacket {
// TODO: get rid of GB import...
// TODO: add operation code
public static class Step1Data {
public byte[] isoSalt;
public byte[] peerAuthId;
public int peerUserType;
public byte[] token;
public Step1Data(JSONObject payload) throws JSONException {
this.isoSalt = GB.hexStringToByteArray(payload.getString("isoSalt"));
this.peerAuthId = GB.hexStringToByteArray(payload.getString("peerAuthId"));
this.peerUserType = payload.getInt("peerUserType");
this.token = GB.hexStringToByteArray(payload.getString("token"));
}
@Override
public String toString() {
return "Step1Data{" +
"isoSalt=" + StringUtils.bytesToHex(isoSalt) +
", peerAuthId=" + StringUtils.bytesToHex(peerAuthId) +
", peerUserType=" + peerUserType +
", token=" + StringUtils.bytesToHex(token) +
'}';
}
}
public static class Step2Data {
public byte[] returnCodeMac;
public Step2Data(JSONObject payload) throws JSONException {
this.returnCodeMac = GB.hexStringToByteArray(payload.getString("returnCodeMac"));
}
@Override
public String toString() {
return "Step2Data{" +
"returnCodeMac=" + StringUtils.bytesToHex(returnCodeMac) +
'}';
}
}
public static class Step3Data {
public byte[] nonce;
public byte[] encAuthToken;
public Step3Data(JSONObject payload) throws JSONException {
this.nonce = GB.hexStringToByteArray(payload.getString("nonce"));
this.encAuthToken = GB.hexStringToByteArray(payload.getString("encAuthToken"));
}
@Override
public String toString() {
return "Step3Data{" +
"nonce=" + StringUtils.bytesToHex(nonce) +
", encAuthToken=" + StringUtils.bytesToHex(encAuthToken) +
'}';
}
}
public static class Step4Data {
public String data;
public Step4Data(HuaweiTLV tlv) {
if (tlv.contains(0x01))
this.data = tlv.getString(0x01);
}
@Override
public String toString() {
return "Step4Data{" +
"data='" + data + '\'' +
'}';
}
}
// TODO: enum?
// 0 is json?
// 2 is raw string?
public byte type;
public JSONObject value;
public JSONObject payload;
public JSONObject version;
public byte step;
// public int operationCode; // TODO
public Step1Data step1Data;
public Step2Data step2Data;
public Step3Data step3Data;
public Step4Data step4Data;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = HiChain.id;
this.isEncrypted = false;
}
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x01))
throw new MissingTagException(0x01);
if (!this.tlv.contains(0x04))
throw new MissingTagException(0x04);
this.type = this.tlv.getByte(0x04);
if (this.type == 0x00) {
try {
this.value = new JSONObject(this.tlv.getString(0x01));
this.payload = value.getJSONObject("payload");
this.version = payload.getJSONObject("version");
// Ugly, but should work
if (payload.has("isoSalt")) {
this.step = 0x01;
this.step1Data = new Step1Data(payload);
} else if (payload.has("returnCodeMac")) {
this.step = 0x02;
this.step2Data = new Step2Data(payload);
} else if (payload.has("encAuthToken")) {
this.step = 0x03;
this.step3Data = new Step3Data(payload);
}
} catch (JSONException e) {
throw new JsonException("", e);
}
} else {
this.step = 0x04;
this.step4Data = new Step4Data(this.tlv);
}
}
}
public static class OutgoingRequest extends HuaweiPacket {
public static class Step1Data {
public byte[] isoSalt;
public byte[] peerAuthId;
public byte[] seed;
public int peerUserType;
String serviceType; // Optional
public Step1Data(JSONObject payload) throws JSONException {
this.isoSalt = GB.hexStringToByteArray(payload.getString("isoSalt"));
this.peerAuthId = GB.hexStringToByteArray(payload.getString("peerAuthId"));
this.seed = GB.hexStringToByteArray(payload.getString("seed"));
this.peerUserType = payload.getInt("peerUserType");
if (payload.has("serviceType"))
this.serviceType = payload.getString("serviceType");
}
@Override
public String toString() {
return "Step1Data{" +
"isoSalt=" + StringUtils.bytesToHex(isoSalt) +
", peerAuthId=" + StringUtils.bytesToHex(peerAuthId) +
", seed=" + StringUtils.bytesToHex(seed) +
", peerUserType=" + peerUserType +
", serviceType='" + serviceType + '\'' +
'}';
}
}
public static class Step2Data {
public byte[] peerAuthId;
public byte[] token;
public boolean isDeviceLevel = false; // Optional
public Step2Data(JSONObject payload) throws JSONException {
this.peerAuthId = GB.hexStringToByteArray(payload.getString("peerAuthId"));
this.token = GB.hexStringToByteArray(payload.getString("token"));
if (payload.has("isDeviceLevel"))
this.isDeviceLevel = payload.getBoolean("isDeviceLevel");
}
@Override
public String toString() {
return "Step2Data{" +
"peerAuthId=" + StringUtils.bytesToHex(peerAuthId) +
", token=" + StringUtils.bytesToHex(token) +
", isDeviceLevel=" + isDeviceLevel +
'}';
}
}
public static class Step3Data {
public byte[] nonce;
public byte[] encData;
public Step3Data(JSONObject payload) throws JSONException {
this.nonce = GB.hexStringToByteArray(payload.getString("nonce"));
this.encData = GB.hexStringToByteArray(payload.getString("encData"));
}
@Override
public String toString() {
return "Step3Data{" +
"nonce=" + StringUtils.bytesToHex(nonce) +
", encData=" + StringUtils.bytesToHex(encData) +
'}';
}
}
public static class Step4Data {
public byte[] nonce;
public byte[] encResult;
public Step4Data(JSONObject payload) throws JSONException {
this.nonce = GB.hexStringToByteArray(payload.getString("nonce"));
this.encResult = GB.hexStringToByteArray(payload.getString("encResult"));
}
@Override
public String toString() {
return "Step4Data{" +
"nonce=" + StringUtils.bytesToHex(nonce) +
", encResult=" + StringUtils.bytesToHex(encResult) +
'}';
}
}
public int step;
public long requestId;
public byte[] selfAuthId;
public String groupId;
public JSONObject version = null;
public JSONObject payload = null;
public JSONObject value = null;
public Step1Data step1Data;
public Step2Data step2Data;
public Step3Data step3Data;
public Step4Data step4Data;
public OutgoingRequest(ParamsProvider paramsProvider) {
super(paramsProvider);
this.complete = false;
}
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x01))
throw new MissingTagException(0x01);
if (!this.tlv.contains(0x02))
throw new MissingTagException(0x02);
try {
value = new JSONObject(this.tlv.getString(0x01));
payload = value.getJSONObject("payload");
version = payload.getJSONObject("version");
if (payload.has("isoSalt")) {
this.step = 1;
this.step1Data = new Step1Data(payload);
} else if (payload.has("token")) {
this.step = 2;
this.step2Data = new Step2Data(payload);
} else if (payload.has("encData")) {
this.step = 3;
this.step3Data = new Step3Data(payload);
} else if (payload.has("encResult")) {
this.step = 4;
this.step4Data = new Step4Data(payload);
}
} catch (JSONException e) {
throw new JsonException("Cannot parse JSON", e);
}
if (this.tlv.contains(0x03))
this.requestId = ByteBuffer.wrap(this.tlv.getBytes(0x03)).getLong();
}
}
}
public static class PinCode {
public static final int id = 0x2C;
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;
this.isEncrypted = false;
}
}
public static class Response extends HuaweiPacket {
public byte[] pinCode;
public Response(HuaweiPacket.ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.isEncrypted = false;
}
@Override
public void parseTlv() throws ParseException {
byte[] message;
byte[] iv;
if (this.tlv.contains(0x01))
message = this.tlv.getBytes(0x01);
else
throw new MissingTagException(0x01);
if (this.tlv.contains(0x02))
iv = this.tlv.getBytes(0x02);
else
throw new MissingTagException(0x02);
HuaweiCrypto huaweiCrypto = new HuaweiCrypto(paramsProvider.getAuthVersion());
try {
pinCode = huaweiCrypto.decryptPinCode(message, iv);
} catch (HuaweiCrypto.CryptoException e) {
throw new CryptoException("Could not decrypt pinCode", e);
}
}
}
// TODO: implement parsing this request for the log parser support
}
public static class SettingRelated {
public static final int id = 0x31;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01)
.put(0x02)
.put(0x03)
.put(0x04)
.put(0x05)
.put(0x06);
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 {
// Tag 1 -> LegalStuff
// Tag 2 -> File support
// Tag 3 -> SmartWatchVersion
// Tag 4 to 6 are HMS related
}
}
}
public static class TimeZoneIdRequest extends HuaweiPacket {
public static final byte id = 0x32;
public TimeZoneIdRequest(
ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
ByteBuffer timeAndZoneId = ByteBuffer.wrap(HuaweiUtil.getTimeAndZoneId());
this.tlv = new HuaweiTLV()
.put(0x01, timeAndZoneId.getInt())
.put(0x02, timeAndZoneId.getShort());
byte[] zoneId = new byte[timeAndZoneId.remaining()];
timeAndZoneId.get(zoneId, 0, timeAndZoneId.remaining());
this.tlv.put(0x03, zoneId);
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class SecurityNegotiation {
public static final int id = 0x33;
public static class Request extends HuaweiPacket {
public Request (
HuaweiPacket.ParamsProvider paramsProvider,
byte authMode,
byte[] deviceUUID,
String phoneModel) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, authMode);
if (authMode == 0x02 || authMode == 0x04)
this.tlv.put(0x02, (byte)0x01);
this.tlv.put(0x05, deviceUUID)
.put(0x03, (byte)0x01)
.put(0x04, (byte)0x00);
if (authMode == 0x04)
this.tlv.put(0x06)
.put(0x07, phoneModel);
this.complete = true;
this.isEncrypted = false;
}
// TODO: implement parsing this request for the log parser support
}
public static class Response extends HuaweiPacket {
public int authType;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
this.authType = -0x1;
int pw = -0x1;
if (this.tlv.contains(0x01)) {
if (this.tlv.getByte(0x01) == 0x01)
this.authType = 0x0186A0;
if (this.tlv.getByte(0x01) == 0x04)
pw = 4;
}
if (this.tlv.contains(0x02)) {
this.authType = (int)this.tlv.getByte(0x02);
if (pw != -0x1)
this.authType ^= pw;
}
if (this.tlv.contains(0x7F))
this.authType = (int)this.tlv.getByte(0x7F);
}
}
}
public static class ConnectStatusRequest extends HuaweiPacket {
public static final int id = 0x35;
public ConnectStatusRequest (HuaweiPacket.ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01,(byte)0x01);
this.complete = true;
}
// TODO: implement parsing this request for the log parser support
}
public static class ExpandCapability {
public static final int id = 0x37;
public static class Request extends HuaweiPacket {
public Request(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 byte[] expandCapabilities;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01)) {
this.expandCapabilities = this.tlv.getBytes(0x01);
} else
throw new MissingTagException(0x01);
}
}
}
public static class WearStatus {
public static final int id = 0x3D;
public static class Request extends HuaweiPacket {
public Request(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);
this.serviceId = DeviceConfig.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
}
}
}
public static class SetUpDeviceStatusRequest extends HuaweiPacket {
public static final int id = 0x3E;
public SetUpDeviceStatusRequest(ParamsProvider paramsProvider, int relationShip, String deviceName) {
super(paramsProvider);
this.serviceId = DeviceConfig.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x01, (byte) relationShip)
.put(0x02, deviceName)
.put(0x03, (byte)0x00);
this.complete = true;
}
}
// TODO: wear location enum?
public static class Date {
// TODO: enum?
public static final int yearFirst = 0x01;
public static final int monthFirst = 0x02;
public static final int dayFirst = 0x03;
}
public static class Time {
// TODO: enum?
public static final int hours12 = 0x01;
public static final int hours24 = 0x02;
}
public static class HiChainStep {
public static final int one = 0x01;
public static final int two = 0x02;
public static final int three = 0x03;
public static final int four = 0x04;
}
}