/HAP/HAPBLECharacteristic+Broadcast.c

https://github.com/apple/HomeKitADK · C · 395 lines · 324 code · 36 blank · 35 comment · 55 complexity · bdaf53cf3b2d3581e3c9677199f6d4b6 MD5 · raw file

  1. // Copyright (c) 2015-2019 The HomeKit ADK Contributors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the “License”);
  4. // you may not use this file except in compliance with the License.
  5. // See [CONTRIBUTORS.md] for the list of HomeKit ADK project authors.
  6. #include "HAP+Internal.h"
  7. static const HAPLogObject logObject = { .subsystem = kHAP_LogSubsystem, .category = "BLECharacteristic" };
  8. HAP_RESULT_USE_CHECK
  9. bool HAPBLECharacteristicIsValidBroadcastInterval(uint8_t value) {
  10. switch (value) {
  11. case kHAPBLECharacteristicBroadcastInterval_20Ms:
  12. case kHAPBLECharacteristicBroadcastInterval_1280Ms:
  13. case kHAPBLECharacteristicBroadcastInterval_2560Ms: {
  14. return true;
  15. }
  16. default: {
  17. return false;
  18. }
  19. }
  20. }
  21. typedef struct {
  22. uint16_t aid;
  23. bool* found;
  24. void* bytes;
  25. size_t maxBytes;
  26. size_t* numBytes;
  27. HAPPlatformKeyValueStoreKey* key;
  28. } GetBroadcastParametersEnumerateContext;
  29. HAP_RESULT_USE_CHECK
  30. static HAPError GetBroadcastConfigurationEnumerateCallback(
  31. void* _Nullable context,
  32. HAPPlatformKeyValueStoreRef keyValueStore,
  33. HAPPlatformKeyValueStoreDomain domain,
  34. HAPPlatformKeyValueStoreKey key,
  35. bool* shouldContinue) {
  36. HAPPrecondition(context);
  37. GetBroadcastParametersEnumerateContext* arguments = context;
  38. HAPPrecondition(arguments->aid);
  39. HAPPrecondition(arguments->aid == 1);
  40. HAPPrecondition(arguments->found);
  41. HAPPrecondition(!*arguments->found);
  42. HAPPrecondition(arguments->bytes);
  43. HAPPrecondition(arguments->maxBytes >= 2);
  44. HAPPrecondition(arguments->numBytes);
  45. HAPPrecondition(arguments->key);
  46. HAPPrecondition(domain == kHAPKeyValueStoreDomain_CharacteristicConfiguration);
  47. HAPPrecondition(shouldContinue);
  48. HAPPrecondition(*shouldContinue);
  49. HAPError err;
  50. // Load.
  51. err = HAPPlatformKeyValueStoreGet(
  52. keyValueStore, domain, key, arguments->bytes, arguments->maxBytes, arguments->numBytes, arguments->found);
  53. if (err) {
  54. HAPAssert(err == kHAPError_Unknown);
  55. return err;
  56. }
  57. HAPAssert(arguments->found);
  58. if (*arguments->numBytes < 2 || *arguments->numBytes == arguments->maxBytes || (*arguments->numBytes - 2) % 3) {
  59. HAPLog(&logObject,
  60. "Invalid characteristic configuration 0x%02X size %lu.",
  61. key,
  62. (unsigned long) *arguments->numBytes);
  63. return kHAPError_Unknown;
  64. }
  65. // Check for match.
  66. if (HAPReadLittleUInt16(arguments->bytes) != arguments->aid) {
  67. *arguments->found = false;
  68. return kHAPError_None;
  69. }
  70. // Match found.
  71. *arguments->key = key;
  72. *shouldContinue = false;
  73. return kHAPError_None;
  74. }
  75. /**
  76. * Fetches the characteristic configuration for an accessory.
  77. *
  78. * @param aid Accessory ID.
  79. * @param[out] found Whether the characteristic configuration has been found.
  80. * @param[out] bytes Buffer to store characteristic configuration, if found.
  81. * @param maxBytes Capacity of buffer. Must be at least 2 + 3 * #<concurrent active broadcasts> + 1.
  82. * @param[out] numBytes Effective length of buffer, if found.
  83. * @param[out] key Key, if found.
  84. * @param keyValueStore Key-value store.
  85. *
  86. * @return kHAPError_None If successful.
  87. * @return kHAPError_Unknown If an I/O error occurred.
  88. */
  89. HAP_RESULT_USE_CHECK
  90. static HAPError GetBroadcastConfiguration(
  91. uint16_t aid,
  92. bool* found,
  93. void* bytes,
  94. size_t maxBytes,
  95. size_t* numBytes,
  96. HAPPlatformKeyValueStoreKey* key,
  97. HAPPlatformKeyValueStoreRef keyValueStore) {
  98. HAPPrecondition(aid);
  99. HAPPrecondition(found);
  100. HAPPrecondition(bytes);
  101. HAPPrecondition(maxBytes >= 3);
  102. HAPPrecondition(numBytes);
  103. HAPPrecondition(key);
  104. HAPPrecondition(keyValueStore);
  105. HAPError err;
  106. *found = false;
  107. GetBroadcastParametersEnumerateContext context = {
  108. .aid = aid, .found = found, .bytes = bytes, .maxBytes = maxBytes, .numBytes = numBytes, .key = key
  109. };
  110. err = HAPPlatformKeyValueStoreEnumerate(
  111. keyValueStore,
  112. kHAPKeyValueStoreDomain_CharacteristicConfiguration,
  113. GetBroadcastConfigurationEnumerateCallback,
  114. &context);
  115. if (err) {
  116. HAPAssert(err == kHAPError_Unknown);
  117. return err;
  118. }
  119. return kHAPError_None;
  120. }
  121. HAP_RESULT_USE_CHECK
  122. HAPError HAPBLECharacteristicGetBroadcastConfiguration(
  123. const HAPCharacteristic* characteristic_,
  124. const HAPService* service,
  125. const HAPAccessory* accessory,
  126. bool* broadcastsEnabled,
  127. HAPBLECharacteristicBroadcastInterval* broadcastInterval,
  128. HAPPlatformKeyValueStoreRef keyValueStore) {
  129. HAPPrecondition(characteristic_);
  130. const HAPBaseCharacteristic* characteristic = characteristic_;
  131. HAPPrecondition(characteristic->properties.ble.supportsBroadcastNotification);
  132. HAPPrecondition(service);
  133. HAPPrecondition(accessory);
  134. HAPPrecondition(broadcastsEnabled);
  135. HAPPrecondition(broadcastInterval);
  136. HAPPrecondition(keyValueStore);
  137. HAPError err;
  138. HAPAssert(accessory->aid == 1);
  139. uint16_t aid = (uint16_t) accessory->aid;
  140. HAPAssert(characteristic->iid <= UINT16_MAX);
  141. uint16_t cid = (uint16_t) characteristic->iid;
  142. // Get configuration.
  143. HAPPlatformKeyValueStoreKey key;
  144. size_t numBytes;
  145. uint8_t bytes[2 + 3 * 42 + 1]; // 128 + 1, allows for 42 concurrent broadcasts on a single KVS key.
  146. bool found;
  147. err = GetBroadcastConfiguration(aid, &found, bytes, sizeof bytes, &numBytes, &key, keyValueStore);
  148. if (err) {
  149. HAPAssert(err == kHAPError_Unknown);
  150. return err;
  151. }
  152. if (!found) {
  153. *broadcastsEnabled = false;
  154. return kHAPError_None;
  155. }
  156. HAPAssert(numBytes >= 2 && !((numBytes - 2) % 3));
  157. HAPAssert(HAPReadLittleUInt16(bytes) == aid);
  158. // Find characteristic.
  159. for (size_t i = 2; i < numBytes; i += 3) {
  160. uint16_t itemCID = HAPReadLittleUInt16(&bytes[i]);
  161. if (itemCID < cid) {
  162. continue;
  163. }
  164. if (itemCID > cid) {
  165. break;
  166. }
  167. // Found. Extract configuration.
  168. uint8_t broadcastConfiguration = bytes[i + 2];
  169. if (!HAPBLECharacteristicIsValidBroadcastInterval(broadcastConfiguration)) {
  170. HAPLogCharacteristic(
  171. &logObject,
  172. characteristic,
  173. service,
  174. accessory,
  175. "Invalid stored broadcast interval: 0x%02x.",
  176. broadcastConfiguration);
  177. return kHAPError_Unknown;
  178. }
  179. *broadcastsEnabled = true;
  180. *broadcastInterval = (HAPBLECharacteristicBroadcastInterval) broadcastConfiguration;
  181. return kHAPError_None;
  182. }
  183. // Not found.
  184. *broadcastsEnabled = false;
  185. return kHAPError_None;
  186. }
  187. HAP_RESULT_USE_CHECK
  188. HAPError HAPBLECharacteristicEnableBroadcastNotifications(
  189. const HAPCharacteristic* characteristic_,
  190. const HAPService* service,
  191. const HAPAccessory* accessory,
  192. HAPBLECharacteristicBroadcastInterval broadcastInterval,
  193. HAPPlatformKeyValueStoreRef keyValueStore) {
  194. HAPPrecondition(characteristic_);
  195. const HAPBaseCharacteristic* characteristic = characteristic_;
  196. HAPPrecondition(characteristic->properties.ble.supportsBroadcastNotification);
  197. HAPPrecondition(service);
  198. HAPPrecondition(accessory);
  199. HAPPrecondition(HAPBLECharacteristicIsValidBroadcastInterval(broadcastInterval));
  200. HAPPrecondition(keyValueStore);
  201. HAPError err;
  202. HAPLogCharacteristicInfo(
  203. &logObject,
  204. characteristic,
  205. service,
  206. accessory,
  207. "Enabling broadcasts (interval = 0x%02x).",
  208. broadcastInterval);
  209. HAPAssert(accessory->aid == 1);
  210. uint16_t aid = (uint16_t) accessory->aid;
  211. HAPAssert(characteristic->iid <= UINT16_MAX);
  212. uint16_t cid = (uint16_t) characteristic->iid;
  213. // Get configuration.
  214. HAPPlatformKeyValueStoreKey key;
  215. size_t numBytes;
  216. uint8_t bytes[2 + 3 * 42 + 1]; // 128 + 1, allows for 42 concurrent broadcasts on a single KVS key.
  217. bool found;
  218. err = GetBroadcastConfiguration(aid, &found, bytes, sizeof bytes, &numBytes, &key, keyValueStore);
  219. if (err) {
  220. HAPAssert(err == kHAPError_Unknown);
  221. return err;
  222. }
  223. if (!found) {
  224. key = 0;
  225. HAPWriteLittleUInt16(bytes, aid);
  226. numBytes = 2;
  227. }
  228. HAPAssert(numBytes >= 2 && !((numBytes - 2) % 3));
  229. HAPAssert(HAPReadLittleUInt16(bytes) == aid);
  230. // Find characteristic.
  231. size_t i;
  232. for (i = 2; i < numBytes; i += 3) {
  233. uint16_t itemCID = HAPReadLittleUInt16(&bytes[i]);
  234. if (itemCID < cid) {
  235. continue;
  236. }
  237. if (itemCID > cid) {
  238. break;
  239. }
  240. // Found. Extract configuration.
  241. uint8_t broadcastConfiguration = bytes[i + 2];
  242. if (!HAPBLECharacteristicIsValidBroadcastInterval(broadcastConfiguration)) {
  243. HAPLogCharacteristic(
  244. &logObject,
  245. characteristic,
  246. service,
  247. accessory,
  248. "Invalid stored broadcast interval: 0x%02x.",
  249. broadcastConfiguration);
  250. return kHAPError_Unknown;
  251. }
  252. // Update configuration.
  253. if ((HAPBLECharacteristicBroadcastInterval) broadcastConfiguration == broadcastInterval) {
  254. return kHAPError_None;
  255. }
  256. bytes[i + 2] = broadcastInterval;
  257. err = HAPPlatformKeyValueStoreSet(
  258. keyValueStore, kHAPKeyValueStoreDomain_CharacteristicConfiguration, key, bytes, numBytes);
  259. if (err) {
  260. HAPAssert(err == kHAPError_Unknown);
  261. return err;
  262. }
  263. return kHAPError_None;
  264. }
  265. // Add configuration.
  266. if (numBytes >= sizeof bytes - 1 - 3) {
  267. HAPLogCharacteristic(
  268. &logObject,
  269. characteristic,
  270. service,
  271. accessory,
  272. "Not enough space to store characteristic configuration.");
  273. return kHAPError_Unknown;
  274. }
  275. HAPRawBufferCopyBytes(&bytes[i + 3], &bytes[i], numBytes - i);
  276. HAPWriteLittleUInt16(&bytes[i], cid);
  277. bytes[i + 2] = broadcastInterval;
  278. numBytes += 3;
  279. err = HAPPlatformKeyValueStoreSet(
  280. keyValueStore, kHAPKeyValueStoreDomain_CharacteristicConfiguration, key, bytes, numBytes);
  281. if (err) {
  282. HAPAssert(err == kHAPError_Unknown);
  283. return err;
  284. }
  285. return kHAPError_None;
  286. }
  287. HAP_RESULT_USE_CHECK
  288. HAPError HAPBLECharacteristicDisableBroadcastNotifications(
  289. const HAPCharacteristic* characteristic_,
  290. const HAPService* service,
  291. const HAPAccessory* accessory,
  292. HAPPlatformKeyValueStoreRef keyValueStore) {
  293. HAPPrecondition(characteristic_);
  294. const HAPBaseCharacteristic* characteristic = characteristic_;
  295. HAPPrecondition(characteristic->properties.ble.supportsBroadcastNotification);
  296. HAPPrecondition(service);
  297. HAPPrecondition(accessory);
  298. HAPPrecondition(keyValueStore);
  299. HAPError err;
  300. HAPLogCharacteristicInfo(&logObject, characteristic, service, accessory, "Disabling broadcasts.");
  301. HAPAssert(accessory->aid == 1);
  302. uint16_t aid = (uint16_t) accessory->aid;
  303. HAPAssert(characteristic->iid <= UINT16_MAX);
  304. uint16_t cid = (uint16_t) characteristic->iid;
  305. // Get configuration.
  306. HAPPlatformKeyValueStoreKey key;
  307. size_t numBytes;
  308. uint8_t bytes[2 + 3 * 42 + 1]; // 128 + 1, allows for 42 concurrent broadcasts on a single KVS key.
  309. bool found;
  310. err = GetBroadcastConfiguration(aid, &found, bytes, sizeof bytes, &numBytes, &key, keyValueStore);
  311. if (err) {
  312. HAPAssert(err == kHAPError_Unknown);
  313. return err;
  314. }
  315. if (!found) {
  316. return kHAPError_None;
  317. }
  318. HAPAssert(numBytes >= 2 && !((numBytes - 2) % 3));
  319. HAPAssert(HAPReadLittleUInt16(bytes) == aid);
  320. // Find characteristic.
  321. size_t i;
  322. for (i = 2; i < numBytes; i += 3) {
  323. uint16_t itemCID = HAPReadLittleUInt16(&bytes[i]);
  324. if (itemCID < cid) {
  325. continue;
  326. }
  327. if (itemCID > cid) {
  328. break;
  329. }
  330. // Found. Extract configuration.
  331. uint8_t broadcastConfiguration = bytes[i + 2];
  332. if (!HAPBLECharacteristicIsValidBroadcastInterval(broadcastConfiguration)) {
  333. HAPLogCharacteristic(
  334. &logObject,
  335. characteristic,
  336. service,
  337. accessory,
  338. "Invalid stored broadcast interval: 0x%02x.",
  339. broadcastConfiguration);
  340. return kHAPError_Unknown;
  341. }
  342. // Remove configuration.
  343. numBytes -= 3;
  344. HAPRawBufferCopyBytes(&bytes[i], &bytes[i + 3], numBytes - i);
  345. if (numBytes == 2) {
  346. err = HAPPlatformKeyValueStoreRemove(
  347. keyValueStore, kHAPKeyValueStoreDomain_CharacteristicConfiguration, key);
  348. } else {
  349. err = HAPPlatformKeyValueStoreSet(
  350. keyValueStore, kHAPKeyValueStoreDomain_CharacteristicConfiguration, key, bytes, numBytes);
  351. }
  352. if (err) {
  353. HAPAssert(err == kHAPError_Unknown);
  354. return err;
  355. }
  356. return kHAPError_None;
  357. }
  358. return kHAPError_None;
  359. }