in HAP/HAPBLEAccessoryServer+Advertising.c [698:1090]
HAPError HAPBLEAccessoryServerDidRaiseEvent(
HAPAccessoryServerRef* server_,
const HAPCharacteristic* characteristic_,
const HAPService* service,
const HAPAccessory* accessory,
HAPSessionRef* _Nullable session) {
HAPPrecondition(server_);
HAPAccessoryServer* server = (HAPAccessoryServer*) server_;
HAPPrecondition(characteristic_);
const HAPBaseCharacteristic* characteristic = characteristic_;
HAPPrecondition(service);
HAPPrecondition(accessory);
HAPPrecondition(accessory->aid == 1);
HAPError err;
if (characteristic->properties.supportsEventNotification) {
// Connected event.
if (server->ble.connection.connected && (!session || session == server->ble.storage->session)) {
const HAPCharacteristic* writtenCharacteristic = server->ble.connection.write.characteristic;
const HAPService* writtenService = server->ble.connection.write.service;
const HAPAccessory* writtenAccessory = server->ble.connection.write.accessory;
if (characteristic_ == writtenCharacteristic && service == writtenService &&
accessory == writtenAccessory) {
HAPLogCharacteristicInfo(
&logObject,
characteristic,
service,
accessory,
"Suppressing notification as the characteristic is currently being written.");
} else {
HAPBLEPeripheralManagerRaiseEvent(server_, characteristic_, service, accessory);
}
}
}
if (characteristic->properties.ble.supportsBroadcastNotification) {
// Broadcasted event.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.6.2 Broadcasted Events
// If a controller connects to the accessory before the completion of the 3 second advertising period the
// accessory should abort the encrypted advertisement and continue with its regular advertisement at the regular
// advertising period after the controller disconnects.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.6.2 Broadcasted Events
if (!server->ble.adv.connected) {
uint16_t keyExpirationGSN;
err = HAPBLEAccessoryServerBroadcastGetParameters(
server->platform.keyValueStore, &keyExpirationGSN, NULL, NULL);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
HAPBLEAccessoryServerGSN gsn;
err = HAPBLEAccessoryServerGetGSN(server->platform.keyValueStore, &gsn);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
// Characteristic changes while in a broadcast encryption key expired state shall not use broadcasted events
// and must fall back to disconnected/connected events until the controller has re-generated a new broadcast
// encryption key and re-registered characteristics for broadcasted notification.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.7.4 Broadcast Encryption Key expiration and refresh
if (keyExpirationGSN && keyExpirationGSN != gsn.gsn) {
HAPBLECharacteristicBroadcastInterval interval;
bool enabled;
err = HAPBLECharacteristicGetBroadcastConfiguration(
characteristic, service, accessory, &enabled, &interval, server->platform.keyValueStore);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
if (enabled) {
// For additional characteristic changes before the completion of the 3 second period and before
// a controller connection, the GSN should be updated again and the accessory must reflect the
// latest changed characteristic value in its encrypted advertisement and continue to broadcast
// for an additional 3 seconds from the last change.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.6.2 Broadcasted Events
if (server->ble.adv.timer) {
HAPPlatformTimerDeregister(server->ble.adv.timer);
server->ble.adv.timer = 0;
}
// Cancel current broadcast.
server->ble.adv.broadcastedEvent.iid = 0;
HAPRawBufferZero(
server->ble.adv.broadcastedEvent.value, sizeof server->ble.adv.broadcastedEvent.value);
HAPAccessoryServerUpdateAdvertisingData(server_);
// Fetch characteristic value.
// When the characteristic value is less than 8 bytes the remaining bytes shall be set to 0.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.2.2 Manufacturer Data
err = kHAPError_Unknown;
uint8_t* bytes = server->ble.adv.broadcastedEvent.value;
HAPAssert(sizeof server->ble.adv.broadcastedEvent.value == 8);
switch (characteristic->format) {
case kHAPCharacteristicFormat_Data: {
// Characteristics with format of string or data/tlv8 cannot be used
// in broadcast notifications
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.2.2 Manufacturer Data
HAPLogCharacteristicError(
&logObject,
characteristic,
service,
accessory,
"%s characteristic cannot be used in broadcast notifications.",
"Data");
} break;
case kHAPCharacteristicFormat_Bool: {
bool value;
err = HAPBoolCharacteristicHandleRead(
server_,
&(const HAPBoolCharacteristicReadRequest) { .transportType = kHAPTransportType_BLE,
.session = NULL,
.characteristic = characteristic_,
.service = service,
.accessory = accessory },
&value,
server->context);
if (err) {
break;
}
bytes[0] = (uint8_t)(value ? 1 : 0);
} break;
case kHAPCharacteristicFormat_UInt8: {
uint8_t value;
err = HAPUInt8CharacteristicHandleRead(
server_,
&(const HAPUInt8CharacteristicReadRequest) { .transportType = kHAPTransportType_BLE,
.session = NULL,
.characteristic = characteristic_,
.service = service,
.accessory = accessory },
&value,
server->context);
if (err) {
break;
}
bytes[0] = value;
} break;
case kHAPCharacteristicFormat_UInt16: {
uint16_t value;
err = HAPUInt16CharacteristicHandleRead(
server_,
&(const HAPUInt16CharacteristicReadRequest) { .transportType =
kHAPTransportType_BLE,
.session = NULL,
.characteristic = characteristic_,
.service = service,
.accessory = accessory },
&value,
server->context);
if (err) {
break;
}
HAPWriteLittleUInt16(bytes, value);
} break;
case kHAPCharacteristicFormat_UInt32: {
uint32_t value;
err = HAPUInt32CharacteristicHandleRead(
server_,
&(const HAPUInt32CharacteristicReadRequest) { .transportType =
kHAPTransportType_BLE,
.session = NULL,
.characteristic = characteristic_,
.service = service,
.accessory = accessory },
&value,
server->context);
if (err) {
break;
}
HAPWriteLittleUInt32(bytes, value);
} break;
case kHAPCharacteristicFormat_UInt64: {
uint64_t value;
err = HAPUInt64CharacteristicHandleRead(
server_,
&(const HAPUInt64CharacteristicReadRequest) { .transportType =
kHAPTransportType_BLE,
.session = NULL,
.characteristic = characteristic_,
.service = service,
.accessory = accessory },
&value,
server->context);
if (err) {
break;
}
HAPWriteLittleUInt64(bytes, value);
} break;
case kHAPCharacteristicFormat_Int: {
int32_t value;
err = HAPIntCharacteristicHandleRead(
server_,
&(const HAPIntCharacteristicReadRequest) { .transportType = kHAPTransportType_BLE,
.session = NULL,
.characteristic = characteristic_,
.service = service,
.accessory = accessory },
&value,
server->context);
if (err) {
break;
}
HAPWriteLittleInt32(bytes, value);
} break;
case kHAPCharacteristicFormat_Float: {
float value;
err = HAPFloatCharacteristicHandleRead(
server_,
&(const HAPFloatCharacteristicReadRequest) { .transportType = kHAPTransportType_BLE,
.session = NULL,
.characteristic = characteristic_,
.service = service,
.accessory = accessory },
&value,
server->context);
if (err) {
break;
}
uint32_t bitPattern = HAPFloatGetBitPattern(value);
HAPWriteLittleUInt32(bytes, bitPattern);
} break;
case kHAPCharacteristicFormat_String: {
// Characteristics with format of string or data/tlv8 cannot be used
// in broadcast notifications.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.2.2 Manufacturer Data
HAPLogCharacteristicError(
&logObject,
characteristic,
service,
accessory,
"%s characteristic cannot be used in broadcast notifications.",
"String");
}
HAPFatalError();
case kHAPCharacteristicFormat_TLV8: {
// Characteristics with format of string or data/tlv8 cannot be used
// in broadcast notifications".
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.2.2.2 Manufacturer Data
HAPLogCharacteristicError(
&logObject,
characteristic,
service,
accessory,
"%s characteristic cannot be used in broadcast notifications.",
"TLV8");
}
HAPFatalError();
}
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState ||
err == kHAPError_OutOfResources || err == kHAPError_Busy);
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Value for broadcast notification could not be received. Skipping event!");
} else {
// Increment GSN.
err = HAPBLEAccessoryServerIncrementGSN(server_);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
err = HAPPlatformTimerRegister(
&server->ble.adv.timer,
HAPPlatformClockGetCurrent() + server->ble.adv.ev_duration,
AdvertisingTimerExpired,
server_);
if (err) {
HAPAssert(err == kHAPError_OutOfResources);
HAPLogCharacteristicError(
&logObject,
characteristic,
service,
accessory,
"Not enough resources to start broadcast event timer. Skipping event!");
} else {
// Initialize broadcasted event.
server->ble.adv.broadcastedEvent.interval = interval;
HAPAssert(characteristic->iid <= UINT16_MAX);
server->ble.adv.broadcastedEvent.iid = (uint16_t) characteristic->iid;
HAPLogCharacteristicInfo(
&logObject, characteristic, service, accessory, "Broadcasted Event.");
}
}
// Update advertisement parameters.
HAPAccessoryServerUpdateAdvertisingData(server_);
return kHAPError_None;
} else {
HAPLogCharacteristicInfo(
&logObject,
characteristic,
service,
accessory,
"Broadcasted Event - Skipping: Broadcasts disabled.");
}
} else {
HAPLogCharacteristicInfo(
&logObject,
characteristic,
service,
accessory,
"Broadcasted Event - Skipping: Broadcast Key expired.");
}
} else {
HAPLogCharacteristicInfo(
&logObject, characteristic, service, accessory, "Broadcasted Event - Skipping: Connected.");
}
}
if (characteristic->properties.ble.supportsDisconnectedNotification) {
// Disconnected event.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.6.3 Disconnected Events
HAPBLEAccessoryServerGSN gsn;
err = HAPBLEAccessoryServerGetGSN(server->platform.keyValueStore, &gsn);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
// The GSN should increment only once for multiple characteristic value changes while in in disconnected state
// until the accessory state changes from disconnected to connected.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.6.3 Disconnected Events
if (!gsn.didIncrement) {
HAPAssert(!server->ble.adv.broadcastedEvent.iid);
HAPAssert(!server->ble.adv.timer);
err = HAPBLEAccessoryServerIncrementGSN(server_);
if (err) {
HAPAssert(err == kHAPError_Unknown);
return err;
}
if (!server->ble.adv.connected) {
HAPLogCharacteristicInfo(&logObject, characteristic, service, accessory, "Disconnected Event.");
// After updating the GSN as specified in Section HAP BLE Regular Advertisement Format in the
// disconnected state the accessory must use a 20 ms advertising interval for at least 3 seconds.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.6.3 Disconnected Events
err = HAPPlatformTimerRegister(
&server->ble.adv.timer,
HAPPlatformClockGetCurrent() + server->ble.adv.ev_duration,
AdvertisingTimerExpired,
server_);
if (err) {
HAPAssert(err == kHAPError_OutOfResources);
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Not enough resources to start disconnected event timer!");
}
// Update advertisement parameters.
HAPAccessoryServerUpdateAdvertisingData(server_);
return kHAPError_None;
} else {
HAPLogCharacteristicInfo(
&logObject, characteristic, service, accessory, "Disconnected Event - Connected (no adv).");
}
} else {
HAPLogCharacteristicInfo(
&logObject,
characteristic,
service,
accessory,
"Disconnected Event - Skipping: GSN already incremented.");
}
}
return kHAPError_None;
}