mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-02-10 19:52:24 +00:00
[Linux] CA state parsing, robuster handshake, persistent window (#94)
* [Linux] Don't quit app when closing window * Add magic pairing functionality * BLE: Allow selecting text * Parse CA state from airpods * Add ability to disable cross-device * More robust handshake/notification request
This commit is contained in:
@@ -38,10 +38,27 @@ namespace AirPodsPackets
|
||||
// Conversational Awareness Packets
|
||||
namespace ConversationalAwareness
|
||||
{
|
||||
static const QByteArray HEADER = QByteArray::fromHex("04000400090028"); // Added for parsing
|
||||
static const QByteArray ENABLED = HEADER + QByteArray::fromHex("01000000");
|
||||
static const QByteArray DISABLED = HEADER + QByteArray::fromHex("02000000");
|
||||
static const QByteArray DATA_HEADER = QByteArray::fromHex("040004004B00020001"); // For received data
|
||||
static const QByteArray HEADER = QByteArray::fromHex("04000400090028"); // For command/status
|
||||
static const QByteArray ENABLED = HEADER + QByteArray::fromHex("01000000"); // Command to enable
|
||||
static const QByteArray DISABLED = HEADER + QByteArray::fromHex("02000000"); // Command to disable
|
||||
static const QByteArray DATA_HEADER = QByteArray::fromHex("040004004B00020001"); // For received speech level data
|
||||
|
||||
static std::optional<bool> parseCAState(const QByteArray &data)
|
||||
{
|
||||
// Extract the status byte (index 7)
|
||||
quint8 statusByte = static_cast<quint8>(data.at(HEADER.size())); // HEADER.size() is 7
|
||||
|
||||
// Interpret the status byte
|
||||
switch (statusByte)
|
||||
{
|
||||
case 0x01: // Enabled
|
||||
return true;
|
||||
case 0x02: // Disabled
|
||||
return false;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connection Packets
|
||||
@@ -88,12 +105,91 @@ namespace AirPodsPackets
|
||||
}
|
||||
}
|
||||
|
||||
namespace MagicPairing {
|
||||
static const QByteArray REQUEST_MAGIC_CLOUD_KEYS = QByteArray::fromHex("0400040030000500");
|
||||
static const QByteArray MAGIC_CLOUD_KEYS_HEADER = QByteArray::fromHex("04000400310002");
|
||||
|
||||
struct MagicCloudKeys {
|
||||
QByteArray magicAccIRK; // 16 bytes
|
||||
QByteArray magicAccEncKey; // 16 bytes
|
||||
};
|
||||
|
||||
inline MagicCloudKeys parseMagicCloudKeysPacket(const QByteArray &data)
|
||||
{
|
||||
MagicCloudKeys keys;
|
||||
|
||||
// Expected size: header (7 bytes) + (1 (tag) + 2 (length) + 1 (reserved) + 16 (value)) * 2 = 47 bytes.
|
||||
if (data.size() < 47)
|
||||
{
|
||||
return keys; // or handle error as needed
|
||||
}
|
||||
|
||||
// Check header
|
||||
if (!data.startsWith(MAGIC_CLOUD_KEYS_HEADER))
|
||||
{
|
||||
return keys; // header mismatch
|
||||
}
|
||||
|
||||
int index = MAGIC_CLOUD_KEYS_HEADER.size(); // Start after header (index 7)
|
||||
|
||||
// --- TLV Block 1 (MagicAccIRK) ---
|
||||
// Tag should be 0x01
|
||||
if (static_cast<quint8>(data.at(index)) != 0x01)
|
||||
{
|
||||
return keys; // unexpected tag
|
||||
}
|
||||
index += 1;
|
||||
|
||||
// Read length (2 bytes, big-endian)
|
||||
quint16 len1 = (static_cast<quint8>(data.at(index)) << 8) | static_cast<quint8>(data.at(index + 1));
|
||||
if (len1 != 16)
|
||||
{
|
||||
return keys; // invalid length
|
||||
}
|
||||
index += 2;
|
||||
|
||||
// Skip reserved byte
|
||||
index += 1;
|
||||
|
||||
// Extract MagicAccIRK (16 bytes)
|
||||
keys.magicAccIRK = data.mid(index, 16);
|
||||
index += 16;
|
||||
|
||||
// --- TLV Block 2 (MagicAccEncKey) ---
|
||||
// Tag should be 0x04
|
||||
if (static_cast<quint8>(data.at(index)) != 0x04)
|
||||
{
|
||||
return keys; // unexpected tag
|
||||
}
|
||||
index += 1;
|
||||
|
||||
// Read length (2 bytes, big-endian)
|
||||
quint16 len2 = (static_cast<quint8>(data.at(index)) << 8) | static_cast<quint8>(data.at(index + 1));
|
||||
if (len2 != 16)
|
||||
{
|
||||
return keys; // invalid length
|
||||
}
|
||||
index += 2;
|
||||
|
||||
// Skip reserved byte
|
||||
index += 1;
|
||||
|
||||
// Extract MagicAccEncKey (16 bytes)
|
||||
keys.magicAccEncKey = data.mid(index, 16);
|
||||
index += 16;
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
// Parsing Headers
|
||||
namespace Parse
|
||||
{
|
||||
static const QByteArray EAR_DETECTION = QByteArray::fromHex("040004000600");
|
||||
static const QByteArray BATTERY_STATUS = QByteArray::fromHex("040004000400");
|
||||
static const QByteArray METADATA = QByteArray::fromHex("040004001d");
|
||||
static const QByteArray HANDSHAKE_ACK = QByteArray::fromHex("01000400");
|
||||
static const QByteArray FEATURES_ACK = QByteArray::fromHex("040004002b00"); // Note: Only tested with airpods pro 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user