in HAP/HAPBLEAccessoryServer+Advertising.c [48:449]
HAPError HAPBLEAccessoryServerGetAdvertisingParameters(
HAPAccessoryServerRef* server_,
bool* isActive,
uint16_t* advertisingInterval,
void* advertisingBytes,
size_t maxAdvertisingBytes,
size_t* numAdvertisingBytes,
void* scanResponseBytes,
size_t maxScanResponseBytes,
size_t* numScanResponseBytes)
HAP_DIAGNOSE_ERROR(maxAdvertisingBytes < 31, "maxAdvertisingBytes must be at least 31")
HAP_DIAGNOSE_WARNING(maxScanResponseBytes < 2, "maxScanResponseBytes should be at least 2") {
HAPPrecondition(server_);
const HAPAccessoryServer* server = (const HAPAccessoryServer*) server_;
HAPPrecondition(isActive);
HAPPrecondition(advertisingInterval);
HAPPrecondition(advertisingBytes);
HAPPrecondition(maxAdvertisingBytes >= 31);
HAPPrecondition(numAdvertisingBytes);
HAPPrecondition(scanResponseBytes);
HAPPrecondition(numScanResponseBytes);
HAPError err;
*isActive = true;
*advertisingInterval = 0;
*numAdvertisingBytes = 0;
*numScanResponseBytes = 0;
// The accessory shall not advertise while it is connected to a HomeKit controller.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.1.4 Advertising Interval
if (server->ble.adv.connected) {
*isActive = false;
return kHAPError_None;
}
if (server->ble.adv.broadcastedEvent.iid) {
// HAP BLE Encrypted Notification Advertisement Format.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.2 HAP BLE Encrypted Notification Advertisement Format
uint16_t keyExpirationGSN;
HAPBLEAccessoryServerBroadcastEncryptionKey broadcastKey;
HAPDeviceID advertisingID;
err = HAPBLEAccessoryServerBroadcastGetParameters(
server->platform.keyValueStore, &keyExpirationGSN, &broadcastKey, &advertisingID);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
if (!keyExpirationGSN) {
HAPLog(&logObject, "Started broadcasted event without valid key. Corrupted data?");
return kHAPError_Unknown;
}
HAPBLEAccessoryServerGSN gsn;
err = HAPBLEAccessoryServerGetGSN(server->platform.keyValueStore, &gsn);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
// Interval.
*advertisingInterval = 0;
switch (server->ble.adv.broadcastedEvent.interval) {
case kHAPBLECharacteristicBroadcastInterval_20Ms: {
*advertisingInterval = HAPBLEAdvertisingIntervalCreateFromMilliseconds(20);
} break;
case kHAPBLECharacteristicBroadcastInterval_1280Ms: {
*advertisingInterval = HAPBLEAdvertisingIntervalCreateFromMilliseconds(1280);
} break;
case kHAPBLECharacteristicBroadcastInterval_2560Ms: {
*advertisingInterval = HAPBLEAdvertisingIntervalCreateFromMilliseconds(2560);
} break;
}
HAPAssert(*advertisingInterval);
uint8_t* adv = advertisingBytes;
// Flags.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.2.1 Flags
/* 0x00 LEN */ *adv++ = 0x02;
/* 0x01 ADT */ *adv++ = 0x01;
/* 0x02 Flags */ *adv++ = 0U << 0U | // NO: LE Limited Discoverable Mode.
1U << 1U | // YES: LE General Discoverable Mode.
1U << 2U | // YES: BR/EDR Not Supported.
0U << 3U | // NO: Simultaneous LE and BR/EDR to Same Device Capable (Controller).
0U << 4U | // NO: Simultaneous LE and BR/EDR to Same Device Capable (Host).
0U << 5U | 0U << 6U | 0U << 7U; // Reserved.
// Manufacturer data.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.2.2 Manufacturer Data
/* 0x00 LEN */ *adv++ = 0x1B;
/* 0x01 ADT */ *adv++ = 0xFF;
/* 0x02 CoID */ HAPWriteLittleUInt16(adv, 0x004CU);
adv += 2;
/* 0x04 TY */ *adv++ = 0x11;
/* 0x05 STL */ *adv++ = 0x36;
/* 0x06 AdvID */ {
HAPAssert(sizeof advertisingID.bytes == 6);
HAPRawBufferCopyBytes(adv, advertisingID.bytes, sizeof advertisingID.bytes);
adv += sizeof advertisingID.bytes;
}
uint8_t* encryptedBytes = adv;
/* 0x0C GSN */ HAPWriteLittleUInt16(adv, gsn.gsn);
adv += 2;
/* 0x0E IID */ HAPWriteLittleUInt16(adv, server->ble.adv.broadcastedEvent.iid);
adv += 2;
/* 0x10 Value */ {
HAPRawBufferCopyBytes(
adv, server->ble.adv.broadcastedEvent.value, sizeof server->ble.adv.broadcastedEvent.value);
adv += sizeof server->ble.adv.broadcastedEvent.value;
}
/* 0x18 Tag */ {
// See HomeKit Accessory Protocol Specification R14
// Section 5.9 AEAD Algorithm.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.7.3 Broadcast Encryption Key Generation
uint8_t tagBytes[CHACHA20_POLY1305_TAG_BYTES];
uint8_t nonceBytes[] = { HAPExpandLittleUInt64((uint64_t) gsn.gsn) };
HAP_chacha20_poly1305_encrypt_aad(
tagBytes,
encryptedBytes,
encryptedBytes,
(size_t)(adv - encryptedBytes),
advertisingID.bytes,
sizeof advertisingID.bytes,
nonceBytes,
sizeof nonceBytes,
broadcastKey.value);
HAPRawBufferCopyBytes(adv, tagBytes, 4);
adv += 4;
}
*numAdvertisingBytes = (size_t)(adv - (uint8_t*) advertisingBytes);
HAPAssert(*numAdvertisingBytes <= maxAdvertisingBytes);
// Log.
adv = advertisingBytes;
adv += 3;
HAPLogInfo(
&logObject,
"HAP BLE Encrypted Notification Advertisement Format (Manufacturer Data).\n"
"- LEN = 0x%02X\n"
"- ADT = 0x%02X\n"
"- CoID = 0x%04X\n"
"- TY = 0x%02X\n"
"- STL = 0x%02X\n"
"- AdvID = %02X:%02X:%02X:%02X:%02X:%02X\n"
"- Ev = 0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n"
" - GSN = %u\n"
" - IID = 0x%04X\n"
" - Value = 0x%02X%02X%02X%02X%02X%02X%02X%02X\n"
"- Tag = 0x%02X%02X%02X%02X",
adv[0x00],
adv[0x01],
HAPReadLittleUInt16(&adv[0x02]),
adv[0x04],
adv[0x05],
adv[0x06],
adv[0x07],
adv[0x08],
adv[0x09],
adv[0x0A],
adv[0x0B],
adv[0x0C],
adv[0x0D],
adv[0x0E],
adv[0x0F],
adv[0x10],
adv[0x11],
adv[0x12],
adv[0x13],
adv[0x14],
adv[0x15],
adv[0x16],
adv[0x17],
gsn.gsn,
server->ble.adv.broadcastedEvent.iid,
server->ble.adv.broadcastedEvent.value[0],
server->ble.adv.broadcastedEvent.value[1],
server->ble.adv.broadcastedEvent.value[2],
server->ble.adv.broadcastedEvent.value[3],
server->ble.adv.broadcastedEvent.value[4],
server->ble.adv.broadcastedEvent.value[5],
server->ble.adv.broadcastedEvent.value[6],
server->ble.adv.broadcastedEvent.value[7],
adv[0x18],
adv[0x19],
adv[0x1A],
adv[0x1B]);
} else {
// HAP BLE Regular Advertisement Format.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.1 HAP BLE Regular Advertisement Format
// Interval.
// - 20 ms for first 30 seconds after boot.
// See Accessory Design Guidelines for Apple Devices R7
// Section 11.5 Advertising Interval
// - 20 ms for first 3 seconds after Disconnected Event.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.6.3 Disconnected Events
// - Regular advertising interval, otherwise.
*advertisingInterval = (server->ble.adv.timer || !server->ble.adv.fast_started || server->ble.adv.fast_timer) ?
HAPBLEAdvertisingIntervalCreateFromMilliseconds(20) :
server->ble.adv.interval;
uint8_t* adv = advertisingBytes;
// Get setup ID.
HAPSetupID setupID;
bool hasSetupID = false;
HAPPlatformAccessorySetupLoadSetupID(server->platform.accessorySetup, &hasSetupID, &setupID);
// Flags.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.1.1 Flags
/* 0x00 LEN */ *adv++ = 0x02;
/* 0x01 ADT */ *adv++ = 0x01;
/* 0x02 Flags */ *adv++ = 0U << 0U | // NO: LE Limited Discoverable Mode.
1U << 1U | // YES: LE General Discoverable Mode.
1U << 2U | // YES: BR/EDR Not Supported.
0U << 3U | // NO: Simultaneous LE and BR/EDR to Same Device Capable (Controller).
0U << 4U | // NO: Simultaneous LE and BR/EDR to Same Device Capable (Host).
0U << 5U | 0U << 6U | 0U << 7U; // Reserved.
// Manufacturer data.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.1.2 Manufacturer Data
/* 0x00 LEN */ *adv++ = (uint8_t)(0x12 + (hasSetupID ? 4 : 0));
/* 0x01 ADT */ *adv++ = 0xFF;
/* 0x02 CoID */ HAPWriteLittleUInt16(adv, 0x004CU);
adv += 2;
/* 0x04 TY */ *adv++ = 0x06;
/* 0x05 STL */ *adv++ = (uint8_t)(0x2D + (hasSetupID ? 4 : 0));
/* 0x06 SF */ *adv++ = (uint8_t)(HAPAccessoryServerIsPaired(server_) ? 0U << 0U : 1U << 0U);
/* 0x07 DevID */ {
HAPDeviceID deviceID;
err = HAPDeviceIDGet(server->platform.keyValueStore, &deviceID);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
HAPAssert(sizeof deviceID.bytes == 6);
HAPRawBufferCopyBytes(adv, deviceID.bytes, sizeof deviceID.bytes);
adv += sizeof deviceID.bytes;
}
/* 0x0D ACID */ HAPWriteLittleUInt16(adv, (uint16_t) server->primaryAccessory->category);
adv += 2;
/* 0x0F GSN */ {
HAPBLEAccessoryServerGSN gsn;
err = HAPBLEAccessoryServerGetGSN(server->platform.keyValueStore, &gsn);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
HAPWriteLittleUInt16(adv, gsn.gsn);
adv += 2;
}
/* 0x11 CN */ {
uint16_t cn;
err = HAPAccessoryServerGetCN(server->platform.keyValueStore, &cn);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
*adv++ = (uint8_t)((cn - 1) % UINT8_MAX + 1);
}
/* 0x12 CV */ *adv++ = 0x02;
/* 0x13 SH */ {
if (hasSetupID) {
// Get Device ID string.
HAPDeviceIDString deviceIDString;
err = HAPDeviceIDGetAsString(server->platform.keyValueStore, &deviceIDString);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
// Get setup hash.
HAPAccessorySetupSetupHash setupHash;
HAPAccessorySetupGetSetupHash(&setupHash, &setupID, &deviceIDString);
// Append.
HAPAssert(sizeof setupHash.bytes == 4);
HAPRawBufferCopyBytes(adv, setupHash.bytes, sizeof setupHash.bytes);
adv += sizeof setupHash.bytes;
}
}
// Name.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.1.3 Local Name
size_t numNameBytes = HAPStringGetNumBytes(server->primaryAccessory->name);
HAPAssert(numNameBytes < UINT8_MAX);
size_t maxNameBytesInAdvertisingData = maxAdvertisingBytes - (size_t)(adv - (uint8_t*) advertisingBytes) - 2;
if (numNameBytes > maxNameBytesInAdvertisingData) {
// When the advertisement includes the shortened local name
// the accessory should include the complete local name in the Scan Response.
if (maxScanResponseBytes >= 2) {
uint8_t* sr = scanResponseBytes;
size_t maxNameBytesInScanResponseData = maxScanResponseBytes - 2;
if (numNameBytes > maxNameBytesInScanResponseData) {
numNameBytes = maxNameBytesInScanResponseData;
/* 0x00 LEN */ *sr++ = (uint8_t)(numNameBytes + 1);
/* 0x01 ADT */ *sr++ = 0x08;
} else {
/* 0x00 LEN */ *sr++ = (uint8_t)(numNameBytes + 1);
/* 0x01 ADT */ *sr++ = 0x09;
}
/* 0x02 Name */ {
HAPRawBufferCopyBytes(sr, server->primaryAccessory->name, numNameBytes);
sr += numNameBytes;
}
*numScanResponseBytes = (size_t)(sr - (uint8_t*) scanResponseBytes);
HAPAssert(*numScanResponseBytes <= maxScanResponseBytes);
}
numNameBytes = maxNameBytesInAdvertisingData;
/* 0x00 LEN */ *adv++ = (uint8_t)(numNameBytes + 1);
/* 0x01 ADT */ *adv++ = 0x08;
} else {
/* 0x00 LEN */ *adv++ = (uint8_t)(numNameBytes + 1);
/* 0x01 ADT */ *adv++ = 0x09;
}
/* 0x02 Name */ {
HAPRawBufferCopyBytes(adv, server->primaryAccessory->name, numNameBytes);
adv += numNameBytes;
}
*numAdvertisingBytes = (size_t)(adv - (uint8_t*) advertisingBytes);
HAPAssert(*numAdvertisingBytes <= maxAdvertisingBytes);
// Log.
adv = advertisingBytes;
adv += 3;
char setupHashLog[12 + 4 * 2 + 1 + 1];
if (hasSetupID) {
err = HAPStringWithFormat(
setupHashLog,
sizeof setupHashLog,
"\n- SH = 0x%02X%02X%02X%02X",
adv[0x13],
adv[0x14],
adv[0x15],
adv[0x16]);
HAPAssert(!err);
}
HAPLogInfo(
&logObject,
"HAP BLE Regular Advertisement Format (Manufacturer Data).\n"
"- LEN = 0x%02X\n"
"- ADT = 0x%02X\n"
"- CoID = 0x%04X\n"
"- TY = 0x%02X\n"
"- STL = 0x%02X\n"
"- SF = 0x%02X\n"
"- DevID = %02X:%02X:%02X:%02X:%02X:%02X\n"
"- ACID = %u\n"
"- GSN = %u\n"
"- CN = %u\n"
"- CV = 0x%02X"
"%s",
adv[0x00],
adv[0x01],
HAPReadLittleUInt16(&adv[0x02]),
adv[0x04],
adv[0x05],
adv[0x06],
adv[0x07],
adv[0x08],
adv[0x09],
adv[0x0A],
adv[0x0B],
adv[0x0C],
HAPReadLittleUInt16(&adv[0x0D]),
HAPReadLittleUInt16(&adv[0x0F]),
adv[0x11],
adv[0x12],
hasSetupID ? setupHashLog : "");
}
float advertisingIntervalMilliseconds = HAPBLEAdvertisingIntervalGetMilliseconds(*advertisingInterval);
HAPLogBufferInfo(
&logObject,
advertisingBytes,
*numAdvertisingBytes,
"ADV data: Active = %d, Interval = %u.%03u ms.",
*isActive,
(uint16_t) advertisingIntervalMilliseconds,
(uint16_t)((uint32_t)(advertisingIntervalMilliseconds * 1000) % 1000));
if (scanResponseBytes && *numScanResponseBytes) {
HAPLogBufferInfo(&logObject, scanResponseBytes, *numScanResponseBytes, "SR data.");
}
return kHAPError_None;
}