HAPError HAPBLEAccessoryServerGetAdvertisingParameters()

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;
}