/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "model/controller/le_advertiser.h"

#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>

#include "hci/address_with_type.h"
#include "log.h"
#include "model/controller/link_layer_controller.h"
#include "packets/hci_packets.h"
#include "packets/link_layer_packets.h"

using namespace bluetooth::hci;
using namespace std::literals;

namespace rootcanal {

namespace chrono {
using duration = std::chrono::steady_clock::duration;
using time_point = std::chrono::steady_clock::time_point;
};  // namespace chrono

slots operator"" _slots(unsigned long long count) { return slots(count); }

// =============================================================================
//  Constants
// =============================================================================

// Vol 6, Part B 搂 4.4.2.4.3 High duty cycle connectable directed advertising.
// NB: The interval is specified to be 3.75ms, but it does not make sense to
// use this value with the timer tick at 5ms.
const chrono::duration adv_direct_ind_high_timeout = 1280ms;
const chrono::duration adv_direct_ind_high_interval = 10ms /*3750us*/;

// Vol 6, Part B 搂 2.3.4.9 Host Advertising Data.
const uint16_t max_legacy_advertising_pdu_size = 31;
const uint16_t max_extended_advertising_pdu_size = 1650;

// =============================================================================
//  Legacy Advertising Commands
// =============================================================================

// HCI command LE_Set_Advertising_Parameters (Vol 4, Part E 搂 7.8.5).
ErrorCode LinkLayerController::LeSetAdvertisingParameters(
    uint16_t advertising_interval_min, uint16_t advertising_interval_max,
    AdvertisingType advertising_type, OwnAddressType own_address_type,
    PeerAddressType peer_address_type, Address peer_address,
    uint8_t advertising_channel_map,
    AdvertisingFilterPolicy advertising_filter_policy) {
  // Legacy advertising commands are disallowed when extended advertising
  // commands were used since the last reset.
  if (!SelectLegacyAdvertising()) {
    INFO(id_,
         "legacy advertising command rejected because extended advertising"
         " is being used");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // Clear reserved bits.
  advertising_channel_map &= 0x7;

  // For high duty cycle directed advertising, i.e. when
  // Advertising_Type is 0x01 (ADV_DIRECT_IND, high duty cycle),
  // the Advertising_Interval_Min and Advertising_Interval_Max parameters
  // are not used and shall be ignored.
  if (advertising_type == AdvertisingType::ADV_DIRECT_IND_HIGH) {
    advertising_interval_min = 0x800;  // Default interval value
    advertising_interval_max = 0x800;
  }

  // The Host shall not issue this command when advertising is enabled in the
  // Controller; if it is the Command Disallowed error code shall be used.
  if (legacy_advertiser_.advertising_enable) {
    INFO(id_, "legacy advertising is enabled");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // At least one channel bit shall be set in the
  // Advertising_Channel_Map parameter.
  if (advertising_channel_map == 0) {
    INFO(id_,
         "advertising_channel_map (0x{:04x}) does not enable any"
         " advertising channel",
         advertising_channel_map);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the advertising interval range provided by the Host
  // (Advertising_Interval_Min, Advertising_Interval_Max) is outside the
  // advertising interval range supported by the Controller, then the
  // Controller shall return the Unsupported Feature or Parameter Value (0x11)
  // error code.
  if (advertising_interval_min < 0x0020 || advertising_interval_min > 0x4000 ||
      advertising_interval_max < 0x0020 || advertising_interval_max > 0x4000) {
    INFO(id_,
         "advertising_interval_min (0x{:04x}) and/or"
         " advertising_interval_max (0x{:04x}) are outside the range"
         " of supported values (0x0020 - 0x4000)",
         advertising_interval_min, advertising_interval_max);
    return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
  }

  // The Advertising_Interval_Min shall be less than or equal to the
  // Advertising_Interval_Max.
  if (advertising_interval_min > advertising_interval_max) {
    INFO(id_,
         "advertising_interval_min (0x{:04x}) is larger than"
         " advertising_interval_max (0x{:04x})",
         advertising_interval_min, advertising_interval_max);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  legacy_advertiser_.advertising_interval =
      advertising_type == AdvertisingType::ADV_DIRECT_IND_HIGH
          ? std::chrono::duration_cast<slots>(adv_direct_ind_high_interval)
          : slots(advertising_interval_min);
  legacy_advertiser_.advertising_type = advertising_type;
  legacy_advertiser_.own_address_type = own_address_type;
  legacy_advertiser_.peer_address_type = peer_address_type;
  legacy_advertiser_.peer_address = peer_address;
  legacy_advertiser_.advertising_channel_map = advertising_channel_map;
  legacy_advertiser_.advertising_filter_policy = advertising_filter_policy;
  return ErrorCode::SUCCESS;
}

// HCI command LE_Set_Advertising_Data (Vol 4, Part E 搂 7.8.7).
ErrorCode LinkLayerController::LeSetAdvertisingData(
    const std::vector<uint8_t>& advertising_data) {
  // Legacy advertising commands are disallowed when extended advertising
  // commands were used since the last reset.
  if (!SelectLegacyAdvertising()) {
    INFO(id_,
         "legacy advertising command rejected because extended advertising"
         " is being used");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  legacy_advertiser_.advertising_data = advertising_data;
  return ErrorCode::SUCCESS;
}

// HCI command LE_Set_Scan_Response_Data (Vol 4, Part E 搂 7.8.8).
ErrorCode LinkLayerController::LeSetScanResponseData(
    const std::vector<uint8_t>& scan_response_data) {
  // Legacy advertising commands are disallowed when extended advertising
  // commands were used since the last reset.
  if (!SelectLegacyAdvertising()) {
    INFO(id_,
         "legacy advertising command rejected because extended advertising"
         " is being used");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  legacy_advertiser_.scan_response_data = scan_response_data;
  return ErrorCode::SUCCESS;
}

// HCI command LE_Advertising_Enable (Vol 4, Part E 搂 7.8.9).
ErrorCode LinkLayerController::LeSetAdvertisingEnable(bool advertising_enable) {
  // Legacy advertising commands are disallowed when extended advertising
  // commands were used since the last reset.
  if (!SelectLegacyAdvertising()) {
    INFO(id_,
         "legacy advertising command rejected because extended advertising"
         " is being used");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  if (!advertising_enable) {
    legacy_advertiser_.Disable();
    return ErrorCode::SUCCESS;
  }

  AddressWithType peer_address = PeerDeviceAddress(
      legacy_advertiser_.peer_address, legacy_advertiser_.peer_address_type);
  AddressWithType public_address{address_, AddressType::PUBLIC_DEVICE_ADDRESS};
  AddressWithType random_address{random_address_,
                                 AddressType::RANDOM_DEVICE_ADDRESS};
  std::optional<AddressWithType> resolvable_address =
      GenerateResolvablePrivateAddress(peer_address, IrkSelection::Local);

  // TODO: additional checks would apply in the case of a LE only Controller
  // with no configured public device address.

  switch (legacy_advertiser_.own_address_type) {
    case OwnAddressType::PUBLIC_DEVICE_ADDRESS:
      legacy_advertiser_.advertising_address = public_address;
      break;

    case OwnAddressType::RANDOM_DEVICE_ADDRESS:
      // If Advertising_Enable is set to 0x01, the advertising parameters'
      // Own_Address_Type parameter is set to 0x01, and the random address for
      // the device has not been initialized using the HCI_LE_Set_Random_Address
      // command, the Controller shall return the error code
      // Invalid HCI Command Parameters (0x12).
      if (random_address.GetAddress() == Address::kEmpty) {
        INFO(id_,
             "own_address_type is Random_Device_Address but the Random_Address"
             " has not been initialized");
        return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
      }
      legacy_advertiser_.advertising_address = random_address;
      break;

    case OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
      legacy_advertiser_.advertising_address =
          resolvable_address.value_or(public_address);
      break;

    case OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
      // If Advertising_Enable is set to 0x01, the advertising parameters'
      // Own_Address_Type parameter is set to 0x03, the controller's resolving
      // list did not contain a matching entry, and the random address for the
      // device has not been initialized using the HCI_LE_Set_Random_Address
      // command, the Controller shall return the error code Invalid HCI Command
      // Parameters (0x12).
      if (resolvable_address) {
        legacy_advertiser_.advertising_address = resolvable_address.value();
      } else if (random_address.GetAddress() == Address::kEmpty) {
        INFO(id_,
             "own_address_type is Resolvable_Or_Random_Address but the"
             " Resolving_List does not contain a matching entry and the"
             " Random_Address is not initialized");
        return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
      } else {
        legacy_advertiser_.advertising_address = random_address;
      }
      break;
  }

  legacy_advertiser_.timeout = {};
  legacy_advertiser_.target_address =
      AddressWithType{Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS};

  switch (legacy_advertiser_.advertising_type) {
    case AdvertisingType::ADV_DIRECT_IND_HIGH:
      // The Link Layer shall exit the Advertising state no later than 1.28 s
      // after the Advertising state was entered.
      legacy_advertiser_.timeout =
          std::chrono::steady_clock::now() + adv_direct_ind_high_timeout;
      [[fallthrough]];

    case AdvertisingType::ADV_DIRECT_IND_LOW: {
      // Note: Vol 6, Part B 搂 6.2.2 Connectable directed event type
      //
      // If an IRK is available in the Link Layer Resolving
      // List for the peer device, then the target鈥檚 device address
      // (TargetA field) shall use a resolvable private address. If an IRK is
      // not available in the Link Layer Resolving List or the IRK is set to
      // zero for the peer device, then the target鈥檚 device address
      // (TargetA field) shall use the Identity Address when entering the
      // Advertising State and using connectable directed events.
      std::optional<AddressWithType> peer_resolvable_address =
          GenerateResolvablePrivateAddress(peer_address, IrkSelection::Peer);
      legacy_advertiser_.target_address =
          peer_resolvable_address.value_or(peer_address);
      break;
    }
    default:
      break;
  }

  legacy_advertiser_.advertising_enable = true;
  legacy_advertiser_.next_event = std::chrono::steady_clock::now() +
                                  legacy_advertiser_.advertising_interval;
  return ErrorCode::SUCCESS;
}

// =============================================================================
//  Extended Advertising Commands
// =============================================================================

// HCI command LE_Set_Advertising_Set_Random_Address (Vol 4, Part E 搂 7.8.52).
ErrorCode LinkLayerController::LeSetAdvertisingSetRandomAddress(
    uint8_t advertising_handle, Address random_address) {
  // If the advertising set corresponding to the Advertising_Handle parameter
  // does not exist, then the Controller shall return the error code
  // Unknown Advertising Identifier (0x42).
  // TODO(c++20) unordered_map<>::contains
  if (extended_advertisers_.count(advertising_handle) == 0) {
    INFO(id_, "no advertising set defined with handle {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
  }

  ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];

  // If the Host issues this command while the advertising set identified by the
  // Advertising_Handle parameter is using connectable advertising and is
  // enabled, the Controller shall return the error code
  // Command Disallowed (0x0C).
  if (advertiser.advertising_enable) {
    INFO(id_, "advertising is enabled for the specified advertising set");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  advertiser.random_address = random_address;
  return ErrorCode::SUCCESS;
}

// HCI command LE_Set_Extended_Advertising_Parameters (Vol 4, Part E 搂 7.8.53).
ErrorCode LinkLayerController::LeSetExtendedAdvertisingParameters(
    uint8_t advertising_handle,
    AdvertisingEventProperties advertising_event_properties,
    uint16_t primary_advertising_interval_min,
    uint16_t primary_advertising_interval_max,
    uint8_t primary_advertising_channel_map, OwnAddressType own_address_type,
    PeerAddressType peer_address_type, Address peer_address,
    AdvertisingFilterPolicy advertising_filter_policy,
    uint8_t advertising_tx_power, PrimaryPhyType primary_advertising_phy,
    uint8_t secondary_max_skip, SecondaryPhyType secondary_advertising_phy,
    uint8_t advertising_sid, bool scan_request_notification_enable) {
  // Extended advertising commands are disallowed when legacy advertising
  // commands were used since the last reset.
  if (!SelectExtendedAdvertising()) {
    INFO(id_,
         "extended advertising command rejected because legacy advertising"
         " is being used");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  bool legacy_advertising = advertising_event_properties.legacy_;
  bool extended_advertising = !advertising_event_properties.legacy_;
  bool connectable_advertising = advertising_event_properties.connectable_;
  bool scannable_advertising = advertising_event_properties.scannable_;
  bool directed_advertising = advertising_event_properties.directed_;
  bool high_duty_cycle_advertising =
      advertising_event_properties.high_duty_cycle_;
  bool anonymous_advertising = advertising_event_properties.anonymous_;
  uint16_t raw_advertising_event_properties =
      ExtendedAdvertiser::GetRawAdvertisingEventProperties(
          advertising_event_properties);

  // Clear reserved bits.
  primary_advertising_channel_map &= 0x7;

  // If the Advertising_Handle does not identify an existing advertising set
  // and the Controller is unable to support a new advertising set at present,
  // the Controller shall return the error code Memory Capacity Exceeded (0x07).
  ExtendedAdvertiser advertiser(advertising_handle);

  // TODO(c++20) unordered_map<>::contains
  if (extended_advertisers_.count(advertising_handle) == 0) {
    if (extended_advertisers_.size() >=
        properties_.le_num_supported_advertising_sets) {
      INFO(id_,
           "no advertising set defined with handle {:02x} and"
           " cannot allocate any more advertisers",
           static_cast<int>(advertising_handle));
      return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
    }
  } else {
    advertiser = extended_advertisers_[advertising_handle];
  }

  // If the Host issues this command when advertising is enabled for the
  // specified advertising set, the Controller shall return the error code
  // Command Disallowed (0x0C).
  if (advertiser.advertising_enable) {
    INFO(id_, "advertising is enabled for the specified advertising set");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If legacy advertising PDU types are being used, then the parameter value
  // shall be one of those specified in Table 7.2.
  if (legacy_advertising &&
      (raw_advertising_event_properties & ~0x10) !=
          static_cast<uint16_t>(LegacyAdvertisingEventProperties::ADV_IND) &&
      (raw_advertising_event_properties & ~0x10) !=
          static_cast<uint16_t>(
              LegacyAdvertisingEventProperties::ADV_DIRECT_IND_LOW) &&
      (raw_advertising_event_properties & ~0x10) !=
          static_cast<uint16_t>(
              LegacyAdvertisingEventProperties::ADV_DIRECT_IND_HIGH) &&
      (raw_advertising_event_properties & ~0x10) !=
          static_cast<uint16_t>(
              LegacyAdvertisingEventProperties::ADV_SCAN_IND) &&
      (raw_advertising_event_properties & ~0x10) !=
          static_cast<uint16_t>(
              LegacyAdvertisingEventProperties::ADV_NONCONN_IND)) {
    INFO(id_,
         "advertising_event_properties (0x{:02x}) is legacy but does not"
         " match valid legacy advertising event types",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  bool can_have_advertising_data =
      (legacy_advertising && !directed_advertising) ||
      (extended_advertising && !scannable_advertising);

  // If the Advertising_Event_Properties parameter [..] specifies a type that
  // does not support advertising data when the advertising set already
  // contains some, the Controller shall return the error code
  // Invalid HCI Command Parameters (0x12).
  if (!can_have_advertising_data && !advertiser.advertising_data.empty()) {
    INFO(id_,
         "advertising_event_properties (0x{:02x}) specifies an event type"
         " that does not support avertising data but the set contains some",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // Note: not explicitly specified in the specification but makes sense
  // in the context of the other checks.
  if (!scannable_advertising && !advertiser.scan_response_data.empty()) {
    INFO(id_,
         "advertising_event_properties (0x{:02x}) specifies an event type"
         " that does not support scan response data but the set contains some",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the advertising set already contains data, the type shall be one that
  // supports advertising data and the amount of data shall not
  // exceed 31 octets.
  if (legacy_advertising &&
      (advertiser.advertising_data.size() > max_legacy_advertising_pdu_size ||
       advertiser.scan_response_data.size() >
           max_legacy_advertising_pdu_size)) {
    INFO(id_,
         "advertising_event_properties (0x{:02x}) is legacy and the"
         " advertising data or scan response data exceeds the capacity"
         " of legacy PDUs",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If extended advertising PDU types are being used (bit 4 = 0) then:
  // The advertisement shall not be both connectable and scannable.
  if (extended_advertising && connectable_advertising &&
      scannable_advertising) {
    INFO(id_,
         "advertising_event_properties (0x{:02x}) is extended and may not"
         " be connectable and scannable at the same time",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // High duty cycle directed connectable advertising (鈮� 3.75 ms
  // advertising interval) shall not be used (bit 3 = 0).
  if (extended_advertising && connectable_advertising && directed_advertising &&
      high_duty_cycle_advertising) {
    INFO(id_,
         "advertising_event_properties (0x{:02x}) is extended and may not"
         " be high-duty cycle directed connectable",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the primary advertising interval range provided by the Host
  // (Primary_Advertising_Interval_Min, Primary_Advertising_Interval_Max) is
  // outside the advertising interval range supported by the Controller, then
  // the Controller shall return the error code Unsupported Feature or
  // Parameter Value (0x11).
  if (primary_advertising_interval_min < 0x20 ||
      primary_advertising_interval_max < 0x20) {
    INFO(id_,
         "primary_advertising_interval_min (0x{:04x}) and/or"
         " primary_advertising_interval_max (0x{:04x}) are outside the range"
         " of supported values (0x0020 - 0xffff)",
         primary_advertising_interval_min, primary_advertising_interval_max);
    return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
  }

  // The Primary_Advertising_Interval_Min parameter shall be less than or equal
  // to the Primary_Advertising_Interval_Max parameter.
  if (primary_advertising_interval_min > primary_advertising_interval_max) {
    INFO(id_,
         "primary_advertising_interval_min (0x{:04x}) is larger than"
         " primary_advertising_interval_max (0x{:04x})",
         primary_advertising_interval_min, primary_advertising_interval_max);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // At least one channel bit shall be set in the
  // Primary_Advertising_Channel_Map parameter.
  if (primary_advertising_channel_map == 0) {
    INFO(id_,
         "primary_advertising_channel_map does not enable any"
         " advertising channel");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If legacy advertising PDUs are being used, the
  // Primary_Advertising_PHY shall indicate the LE 1M PHY.
  if (legacy_advertising && primary_advertising_phy != PrimaryPhyType::LE_1M) {
    INFO(id_,
         "advertising_event_properties (0x{:04x}) is legacy but"
         " primary_advertising_phy ({:02x}) is not LE 1M",
         raw_advertising_event_properties,
         static_cast<uint8_t>(primary_advertising_phy));
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If Constant Tone Extensions are enabled for the advertising set and
  // Secondary_Advertising_PHY specifies a PHY that does not allow
  // Constant Tone Extensions, the Controller shall
  // return the error code Command Disallowed (0x0C).
  if (advertiser.constant_tone_extensions &&
      secondary_advertising_phy == SecondaryPhyType::LE_CODED) {
    INFO(id_,
         "constant tone extensions are enabled but"
         " secondary_advertising_phy ({:02x}) does not support them",
         static_cast<uint8_t>(secondary_advertising_phy));
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If the Host issues this command when periodic advertising is enabled for
  // the specified advertising set and connectable, scannable, legacy,
  // or anonymous advertising is specified, the Controller shall return the
  // error code Invalid HCI Command Parameters (0x12).
  if (advertiser.periodic_advertising_enable &&
      (connectable_advertising || scannable_advertising || legacy_advertising ||
       anonymous_advertising)) {
    INFO(id_,
         "periodic advertising is enabled for the specified advertising set"
         " and advertising_event_properties (0x{:02x}) is either"
         " connectable, scannable, legacy, or anonymous",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If periodic advertising is enabled for the advertising set and the
  // Secondary_Advertising_PHY parameter does not specify the PHY currently
  // being used for the periodic advertising, the Controller shall return the
  // error code Command Disallowed (0x0C).
#if 0
  if (advertiser.periodic_advertising_enable) {
    // TODO
    INFO(id_,
        "periodic advertising is enabled for the specified advertising set"
        " and the secondary PHY does not match the periodic"
        " advertising PHY");
    return ErrorCode::COMMAND_DISALLOWED;
  }
#endif

  // If the advertising set already contains advertising data or scan response
  // data, extended advertising is being used, and the length of the data is
  // greater than the maximum that the Controller can transmit within the
  // longest possible auxiliary advertising segment consistent with the
  // parameters, the Controller shall return the error code
  // Packet Too Long (0x45). If advertising on the LE Coded PHY, the S=8
  // coding shall be assumed.
  if (extended_advertising &&
      (advertiser.advertising_data.size() > max_extended_advertising_pdu_size ||
       advertiser.scan_response_data.size() >
           max_extended_advertising_pdu_size)) {
    INFO(id_,
         "the advertising data contained in the set is larger than the"
         " available PDU capacity");
    return ErrorCode::PACKET_TOO_LONG;
  }

  advertiser.advertising_event_properties = advertising_event_properties;
  advertiser.primary_advertising_interval =
      slots(primary_advertising_interval_min);
  advertiser.primary_advertising_channel_map = primary_advertising_channel_map;
  advertiser.own_address_type = own_address_type;
  advertiser.peer_address_type = peer_address_type;
  advertiser.peer_address = peer_address;
  advertiser.advertising_filter_policy = advertising_filter_policy;
  advertiser.advertising_tx_power = advertising_tx_power;
  advertiser.primary_advertising_phy = primary_advertising_phy;
  advertiser.secondary_max_skip = secondary_max_skip;
  advertiser.secondary_advertising_phy = secondary_advertising_phy;
  advertiser.advertising_sid = advertising_sid;
  advertiser.scan_request_notification_enable =
      scan_request_notification_enable;

  extended_advertisers_.insert_or_assign(advertising_handle,
                                         std::move(advertiser));
  return ErrorCode::SUCCESS;
}

// HCI command LE_Set_Extended_Advertising_Data (Vol 4, Part E 搂 7.8.54).
ErrorCode LinkLayerController::LeSetExtendedAdvertisingData(
    uint8_t advertising_handle, Operation operation,
    FragmentPreference fragment_preference,
    const std::vector<uint8_t>& advertising_data) {
  // Extended advertising commands are disallowed when legacy advertising
  // commands were used since the last reset.
  if (!SelectExtendedAdvertising()) {
    INFO(id_,
         "extended advertising command rejected because legacy advertising"
         " is being used");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // fragment_preference is unused for now.
  (void)fragment_preference;

  // If the advertising set corresponding to the Advertising_Handle parameter
  // does not exist, then the Controller shall return the error code
  // Unknown Advertising Identifier (0x42).
  // TODO(c++20) unordered_map<>::contains
  if (extended_advertisers_.count(advertising_handle) == 0) {
    INFO(id_, "no advertising set defined with handle {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
  }

  ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];
  const AdvertisingEventProperties& advertising_event_properties =
      advertiser.advertising_event_properties;
  uint16_t raw_advertising_event_properties =
      ExtendedAdvertiser::GetRawAdvertisingEventProperties(
          advertising_event_properties);

  bool can_have_advertising_data = (advertising_event_properties.legacy_ &&
                                    !advertising_event_properties.directed_) ||
                                   (!advertising_event_properties.legacy_ &&
                                    !advertising_event_properties.scannable_);

  // If the advertising set specifies a type that does not support
  // advertising data, the Controller shall return the error code
  // Invalid HCI Command Parameters (0x12).
  if (!can_have_advertising_data) {
    INFO(id_,
         "advertising_event_properties ({:02x}) does not support"
         " advertising data",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the advertising set uses legacy advertising PDUs that support
  // advertising data and either Operation is not 0x03 or the
  // Advertising_Data_Length parameter exceeds 31 octets, the Controller
  // shall return the error code Invalid HCI Command Parameters (0x12).
  if (advertising_event_properties.legacy_ &&
      (operation != Operation::COMPLETE_ADVERTISEMENT ||
       advertising_data.size() > max_legacy_advertising_pdu_size)) {
    INFO(id_,
         "advertising_event_properties ({:02x}) is legacy and"
         " and an incomplete operation was used or the advertising data"
         " is larger than 31",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If Operation is 0x04 and:
  //    鈥� advertising is currently disabled for the advertising set;
  //    鈥� the advertising set contains no data;
  //    鈥� the advertising set uses legacy PDUs; or
  //    鈥� Advertising_Data_Length is not zero;
  // then the Controller shall return the error code Invalid HCI Command
  // Parameters (0x12).
  if (operation == Operation::UNCHANGED_DATA &&
      (!advertiser.advertising_enable || advertiser.advertising_data.empty() ||
       advertising_event_properties.legacy_ || !advertising_data.empty())) {
    INFO(id_,
         "Unchanged_Data operation is used but advertising is disabled;"
         " or the advertising set contains no data;"
         " or the advertising set uses legacy PDUs;"
         " or the advertising data is not empty");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If Operation is not 0x03 or 0x04 and Advertising_Data_Length is zero,
  // the Controller shall return the error code Invalid HCI
  // Command Parameters (0x12).
  if (operation != Operation::COMPLETE_ADVERTISEMENT &&
      operation != Operation::UNCHANGED_DATA && advertising_data.empty()) {
    INFO(id_,
         "operation ({:02x}) is not Complete_Advertisement or Unchanged_Data"
         " but the advertising data is empty",
         static_cast<int>(operation));
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If advertising is currently enabled for the specified advertising set and
  // Operation does not have the value 0x03 or 0x04, the Controller shall
  // return the error code Command Disallowed (0x0C).
  if (advertiser.advertising_enable &&
      operation != Operation::COMPLETE_ADVERTISEMENT &&
      operation != Operation::UNCHANGED_DATA) {
    INFO(id_,
         "operation ({:02x}) is used but advertising is enabled for the"
         " specified advertising set",
         static_cast<int>(operation));
    return ErrorCode::COMMAND_DISALLOWED;
  }

  switch (operation) {
    case Operation::INTERMEDIATE_FRAGMENT:
      advertiser.advertising_data.insert(advertiser.advertising_data.end(),
                                         advertising_data.begin(),
                                         advertising_data.end());
      advertiser.partial_advertising_data = true;
      break;

    case Operation::FIRST_FRAGMENT:
      advertiser.advertising_data = advertising_data;
      advertiser.partial_advertising_data = true;
      break;

    case Operation::LAST_FRAGMENT:
      advertiser.advertising_data.insert(advertiser.advertising_data.end(),
                                         advertising_data.begin(),
                                         advertising_data.end());
      advertiser.partial_advertising_data = false;
      break;

    case Operation::COMPLETE_ADVERTISEMENT:
      advertiser.advertising_data = advertising_data;
      advertiser.partial_advertising_data = false;
      break;

    case Operation::UNCHANGED_DATA:
      break;

    default:
      INFO(id_, "unknown operation ({})", static_cast<int>(operation));
      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the combined length of the data exceeds the capacity of the
  // advertising set identified by the Advertising_Handle parameter
  // (see Section 7.8.57 LE Read Maximum Advertising Data Length command)
  // or the amount of memory currently available, all the data
  // shall be discarded and the Controller shall return the error code Memory
  // Capacity Exceeded (0x07).
  if (advertiser.advertising_data.size() >
      properties_.le_max_advertising_data_length) {
    INFO(id_,
         "the combined length {} of the advertising data exceeds the"
         " advertising set capacity {}",
         advertiser.advertising_data.size(),
         properties_.le_max_advertising_data_length);
    advertiser.advertising_data.clear();
    advertiser.partial_advertising_data = false;
    return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
  }

  // If advertising is currently enabled for the specified advertising set,
  // the advertising set uses extended advertising, and the length of the
  // data is greater than the maximum that the Controller can transmit within
  // the longest possible auxiliary advertising segment consistent with the
  // current parameters of the advertising set, the Controller shall return
  // the error code Packet Too Long (0x45). If advertising on the
  // LE Coded PHY, the S=8 coding shall be assumed.
  size_t max_advertising_data_length =
      ExtendedAdvertiser::GetMaxAdvertisingDataLength(
          advertising_event_properties);
  if (advertiser.advertising_enable &&
      advertiser.advertising_data.size() > max_advertising_data_length) {
    INFO(id_,
         "the advertising data contained in the set is larger than the"
         " available PDU capacity");
    advertiser.advertising_data.clear();
    advertiser.partial_advertising_data = false;
    return ErrorCode::PACKET_TOO_LONG;
  }

  return ErrorCode::SUCCESS;
}

// HCI command LE_Set_Extended_Scan_Response_Data (Vol 4, Part E 搂 7.8.55).
ErrorCode LinkLayerController::LeSetExtendedScanResponseData(
    uint8_t advertising_handle, Operation operation,
    FragmentPreference fragment_preference,
    const std::vector<uint8_t>& scan_response_data) {
  // Extended advertising commands are disallowed when legacy advertising
  // commands were used since the last reset.
  if (!SelectExtendedAdvertising()) {
    INFO(id_,
         "extended advertising command rejected because legacy advertising"
         " is being used");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // fragment_preference is unused for now.
  (void)fragment_preference;

  // If the advertising set corresponding to the Advertising_Handle parameter
  // does not exist, then the Controller shall return the error code
  // Unknown Advertising Identifier (0x42).
  // TODO(c++20) unordered_map<>::contains
  if (extended_advertisers_.count(advertising_handle) == 0) {
    INFO(id_, "no advertising set defined with handle {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
  }

  ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];
  const AdvertisingEventProperties& advertising_event_properties =
      advertiser.advertising_event_properties;
  uint16_t raw_advertising_event_properties =
      ExtendedAdvertiser::GetRawAdvertisingEventProperties(
          advertising_event_properties);

  // If the advertising set is non-scannable and the Host uses this
  // command other than to discard existing data, the Controller shall
  // return the error code Invalid HCI Command Parameters (0x12).
  if (!advertising_event_properties.scannable_ && !scan_response_data.empty()) {
    INFO(id_,
         "advertising_event_properties ({:02x}) is not scannable"
         " but the scan response data is not empty",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the advertising set uses scannable legacy advertising PDUs and
  // either Operation is not 0x03 or the Scan_Response_Data_Length
  // parameter exceeds 31 octets, the Controller shall
  // return the error code Invalid HCI Command Parameters (0x12).
  if (advertising_event_properties.scannable_ &&
      advertising_event_properties.legacy_ &&
      (operation != Operation::COMPLETE_ADVERTISEMENT ||
       scan_response_data.size() > max_legacy_advertising_pdu_size)) {
    INFO(id_,
         "advertising_event_properties ({:02x}) is scannable legacy"
         " and an incomplete operation was used or the scan response data"
         " is larger than 31",
         raw_advertising_event_properties);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If Operation is not 0x03 and Scan_Response_Data_Length is zero, the
  // Controller shall return the error code
  // Invalid HCI Command Parameters (0x12).
  if (operation != Operation::COMPLETE_ADVERTISEMENT &&
      scan_response_data.empty()) {
    INFO(id_,
         "operation ({:02x}) is not Complete_Advertisement but the"
         " scan response data is empty",
         static_cast<int>(operation));
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If advertising is currently enabled for the specified advertising set and
  // Operation does not have the value 0x03, the Controller shall
  // return the error code Command Disallowed (0x0C).
  if (advertiser.advertising_enable &&
      operation != Operation::COMPLETE_ADVERTISEMENT) {
    INFO(id_,
         "operation ({:02x}) is used but advertising is enabled for the"
         " specified advertising set",
         static_cast<int>(operation));
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If the advertising set uses scannable extended advertising PDUs,
  // advertising is currently enabled for the specified advertising set,
  // and Scan_Response_Data_Length is zero, the Controller shall return
  // the error code Command Disallowed (0x0C).
  if (advertiser.advertising_enable &&
      advertising_event_properties.scannable_ &&
      !advertising_event_properties.legacy_ && scan_response_data.empty()) {
    INFO(id_,
         "advertising_event_properties ({:02x}) is scannable extended,"
         " advertising is enabled for the specified advertising set"
         " and the scan response data is empty",
         raw_advertising_event_properties);
    return ErrorCode::COMMAND_DISALLOWED;
  }

  switch (operation) {
    case Operation::INTERMEDIATE_FRAGMENT:
      advertiser.scan_response_data.insert(advertiser.scan_response_data.end(),
                                           scan_response_data.begin(),
                                           scan_response_data.end());
      advertiser.partial_scan_response_data = true;
      break;

    case Operation::FIRST_FRAGMENT:
      advertiser.scan_response_data = scan_response_data;
      advertiser.partial_scan_response_data = true;
      break;

    case Operation::LAST_FRAGMENT:
      advertiser.scan_response_data.insert(advertiser.scan_response_data.end(),
                                           scan_response_data.begin(),
                                           scan_response_data.end());
      advertiser.partial_scan_response_data = false;
      break;

    case Operation::COMPLETE_ADVERTISEMENT:
      advertiser.scan_response_data = scan_response_data;
      advertiser.partial_scan_response_data = false;
      break;

    case Operation::UNCHANGED_DATA:
      INFO(id_,
           "the operation Unchanged_Data is only allowed"
           " for Advertising_Data");
      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;

    default:
      INFO(id_, "unknown operation ({})", static_cast<int>(operation));
      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the combined length of the data exceeds the capacity of the
  // advertising set identified by the Advertising_Handle parameter
  // (see Section 7.8.57 LE Read Maximum Advertising Data Length command)
  // or the amount of memory currently available, all the data shall be
  // discarded and the Controller shall return the error code
  // Memory Capacity Exceeded (0x07).
  if (advertiser.scan_response_data.size() >
      properties_.le_max_advertising_data_length) {
    INFO(id_,
         "the combined length of the scan response data exceeds the"
         " advertising set capacity");
    advertiser.scan_response_data.clear();
    advertiser.partial_scan_response_data = false;
    return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
  }

  // If the advertising set uses extended advertising and the combined length
  // of the data is greater than the maximum that the Controller can transmit
  // within the longest possible auxiliary advertising segment consistent
  // with the current parameters of the advertising set (using the current
  // advertising interval if advertising is enabled), all the data shall be
  // discarded and the Controller shall return the error code
  // Packet Too Long (0x45). If advertising on the LE Coded PHY,
  // the S=8 coding shall be assumed.
  if (advertiser.scan_response_data.size() >
      max_extended_advertising_pdu_size) {
    INFO(id_,
         "the scan response data contained in the set is larger than the"
         " available PDU capacity");
    advertiser.scan_response_data.clear();
    advertiser.partial_scan_response_data = false;
    return ErrorCode::PACKET_TOO_LONG;
  }

  return ErrorCode::SUCCESS;
}

// HCI command LE_Set_Extended_Advertising_Enable (Vol 4, Part E 搂 7.8.56).
ErrorCode LinkLayerController::LeSetExtendedAdvertisingEnable(
    bool enable, const std::vector<bluetooth::hci::EnabledSet>& sets) {
  // Extended advertising commands are disallowed when legacy advertising
  // commands were used since the last reset.
  if (!SelectExtendedAdvertising()) {
    INFO(id_,
         "extended advertising command rejected because legacy advertising"
         " is being used");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // Validate the advertising handles.
  std::array<bool, UINT8_MAX> used_advertising_handles{};
  for (auto& set : sets) {
    // If the same advertising set is identified by more than one entry in the
    // Advertising_Handle[i] arrayed parameter, then the Controller shall return
    // the error code Invalid HCI Command Parameters (0x12).
    if (used_advertising_handles[set.advertising_handle_]) {
      INFO(id_, "advertising handle {:02x} is added more than once",
           set.advertising_handle_);
      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
    }

    // If the advertising set corresponding to the Advertising_Handle[i]
    // parameter does not exist, then the Controller shall return the error code
    // Unknown Advertising Identifier (0x42).
    if (extended_advertisers_.find(set.advertising_handle_) ==
        extended_advertisers_.end()) {
      INFO(id_, "advertising handle {:02x} is not defined",
           set.advertising_handle_);
      return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
    }

    used_advertising_handles[set.advertising_handle_] = true;
  }

  // If Enable and Num_Sets are both set to
  // 0x00, then all advertising sets are disabled.
  if (!enable && sets.empty()) {
    for (auto& [_, advertiser] : extended_advertisers_) {
      advertiser.Disable();
    }
    return ErrorCode::SUCCESS;
  }

  // If Num_Sets is set to 0x00, the Controller shall return the error code
  // Invalid HCI Command Parameters (0x12).
  if (sets.empty()) {
    INFO(id_, "enable is true but no advertising set is selected");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // No additional checks for disabling advertising sets.
  if (!enable) {
    for (auto& set : sets) {
      auto& advertiser = extended_advertisers_[set.advertising_handle_];
      advertiser.Disable();
    }
    return ErrorCode::SUCCESS;
  }

  // Validate the advertising parameters before enabling any set.
  for (auto& set : sets) {
    ExtendedAdvertiser& advertiser =
        extended_advertisers_[set.advertising_handle_];
    const AdvertisingEventProperties& advertising_event_properties =
        advertiser.advertising_event_properties;

    bool extended_advertising = !advertising_event_properties.legacy_;
    bool connectable_advertising = advertising_event_properties.connectable_;
    bool scannable_advertising = advertising_event_properties.scannable_;
    bool directed_advertising = advertising_event_properties.directed_;
    bool high_duty_cycle_advertising =
        advertising_event_properties.high_duty_cycle_;

    // If the advertising is high duty cycle connectable directed advertising,
    // then Duration[i] shall be less than or equal to 1.28 seconds and shall
    // not be equal to 0.
    std::chrono::milliseconds duration =
        std::chrono::milliseconds(set.duration_ * 10);
    if (connectable_advertising && directed_advertising &&
        high_duty_cycle_advertising &&
        (set.duration_ == 0 || duration > adv_direct_ind_high_timeout)) {
      INFO(id_,
           "extended advertising is high duty cycle connectable directed"
           " but the duration is either 0 or larger than 1.28 seconds");
      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
    }

    // If the advertising set contains partial advertising data or partial
    // scan response data, the Controller shall return the error code
    // Command Disallowed (0x0C).
    if (advertiser.partial_advertising_data ||
        advertiser.partial_scan_response_data) {
      INFO(id_,
           "advertising set contains partial advertising"
           " or scan response data");
      return ErrorCode::COMMAND_DISALLOWED;
    }

    // If the advertising set uses scannable extended advertising PDUs and no
    // scan response data is currently provided, the Controller shall return the
    // error code Command Disallowed (0x0C).
    if (extended_advertising && scannable_advertising &&
        advertiser.scan_response_data.empty()) {
      INFO(id_,
           "advertising set uses scannable extended advertising PDUs"
           " but no scan response data is provided");
      return ErrorCode::COMMAND_DISALLOWED;
    }

    // If the advertising set uses connectable extended advertising PDUs and the
    // advertising data in the advertising set will not fit in the
    // AUX_ADV_IND PDU, the Controller shall return the error code
    // Invalid HCI Command Parameters (0x12).
    if (extended_advertising && connectable_advertising &&
        advertiser.advertising_data.size() >
            ExtendedAdvertiser::GetMaxAdvertisingDataLength(
                advertising_event_properties)) {
      INFO(id_,
           "advertising set uses connectable extended advertising PDUs"
           " but the advertising data does not fit in AUX_ADV_IND PDUs");
      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
    }

    // If extended advertising is being used and the length of any advertising
    // data or of any scan response data is greater than the maximum that the
    // Controller can transmit within the longest possible auxiliary
    // advertising segment consistent with the chosen advertising interval,
    // the Controller shall return the error code Packet Too Long (0x45).
    // If advertising on the LE Coded PHY, the S=8 coding shall be assumed.
    if (extended_advertising && (advertiser.advertising_data.size() >
                                     max_extended_advertising_pdu_size ||
                                 advertiser.scan_response_data.size() >
                                     max_extended_advertising_pdu_size)) {
      INFO(id_,
           "advertising set uses extended advertising PDUs"
           " but the advertising data does not fit in advertising PDUs");
      return ErrorCode::PACKET_TOO_LONG;
    }

    AddressWithType peer_address = PeerDeviceAddress(
        advertiser.peer_address, advertiser.peer_address_type);
    AddressWithType public_address{address_,
                                   AddressType::PUBLIC_DEVICE_ADDRESS};
    AddressWithType random_address{
        advertiser.random_address.value_or(Address::kEmpty),
        AddressType::RANDOM_DEVICE_ADDRESS};
    std::optional<AddressWithType> resolvable_address =
        GenerateResolvablePrivateAddress(peer_address, IrkSelection::Local);

    // TODO: additional checks would apply in the case of a LE only Controller
    // with no configured public device address.

    switch (advertiser.own_address_type) {
      case OwnAddressType::PUBLIC_DEVICE_ADDRESS:
        advertiser.advertising_address = public_address;
        break;

      case OwnAddressType::RANDOM_DEVICE_ADDRESS:
        // If the advertising set's Own_Address_Type parameter is set to 0x01
        // and the random address for the advertising set has not been
        // initialized using the HCI_LE_Set_Advertising_Set_Random_Address
        // command, the Controller shall return the error code
        // Invalid HCI Command Parameters (0x12).
        if (random_address.GetAddress() == Address::kEmpty) {
          INFO(
              id_,
              "own_address_type is Random_Device_Address but the Random_Address"
              " has not been initialized");
          return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
        }
        advertiser.advertising_address = random_address;
        break;

      case OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
        advertiser.advertising_address =
            resolvable_address.value_or(public_address);
        break;

      case OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
        // If the advertising set's Own_Address_Type parameter is set to 0x03,
        // the controller's resolving list did not contain a matching entry,
        // and the random address for the advertising set has not been
        // initialized using the HCI_LE_Set_Advertising_Set_Random_Address
        // command, the Controller shall return the error code
        // Invalid HCI Command Parameters (0x12).
        if (resolvable_address) {
          advertiser.advertising_address = resolvable_address.value();
        } else if (random_address.GetAddress() == Address::kEmpty) {
          INFO(id_,
               "own_address_type is Resolvable_Or_Random_Address but the"
               " Resolving_List does not contain a matching entry and the"
               " Random_Address is not initialized");
          return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
        } else {
          advertiser.advertising_address = random_address;
        }
        break;
    }

    // If an IRK is available in the Link Layer Resolving List for the peer
    // device, then the target鈥檚 device address (TargetA field) shall
    // use a resolvable private address. If an IRK is not available in the
    // Link Layer Resolving List or the IRK is set to zero for the peer device,
    // then the target鈥檚 device address (TargetA field) shall use the Identity
    // Address when entering the Advertising State and using connectable
    // directed events.
    if (advertiser.IsDirected()) {
      advertiser.target_address =
          GenerateResolvablePrivateAddress(peer_address, IrkSelection::Peer)
              .value_or(peer_address);
    }
  }

  for (auto& set : sets) {
    ExtendedAdvertiser& advertiser =
        extended_advertisers_[set.advertising_handle_];

    advertiser.max_extended_advertising_events =
        set.max_extended_advertising_events_;
    advertiser.num_completed_extended_advertising_events = 0;
    advertiser.Enable();
    if (set.duration_ > 0) {
      std::chrono::milliseconds duration =
          std::chrono::milliseconds(set.duration_ * 10);
      advertiser.timeout = std::chrono::steady_clock::now() + duration;
    } else {
      advertiser.timeout.reset();
    }
  }

  return ErrorCode::SUCCESS;
}

// HCI command LE_Remove_Advertising_Set (Vol 4, Part E 搂 7.8.59).
ErrorCode LinkLayerController::LeRemoveAdvertisingSet(
    uint8_t advertising_handle) {
  // If the advertising set corresponding to the Advertising_Handle parameter
  // does not exist, then the Controller shall return the error code
  // Unknown Advertising Identifier (0x42).
  auto advertiser = extended_advertisers_.find(advertising_handle);
  if (advertiser == extended_advertisers_.end()) {
    INFO(id_, "no advertising set defined with handle {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
  }

  // If advertising or periodic advertising on the advertising set is
  // enabled, then the Controller shall return the error code
  // Command Disallowed (0x0C).
  if (advertiser->second.advertising_enable) {
    INFO(id_, "the advertising set defined with handle {:02x} is enabled",
         static_cast<int>(advertising_handle));
    return ErrorCode::COMMAND_DISALLOWED;
  }

  extended_advertisers_.erase(advertiser);
  return ErrorCode::SUCCESS;
}

// HCI command LE_Clear_Advertising_Sets (Vol 4, Part E 搂 7.8.60).
ErrorCode LinkLayerController::LeClearAdvertisingSets() {
  // If advertising or periodic advertising is enabled on any advertising set,
  // then the Controller shall return the error code Command Disallowed (0x0C).
  for (auto& advertiser : extended_advertisers_) {
    if (advertiser.second.advertising_enable) {
      INFO(id_, "the advertising set with handle {:02x} is enabled",
           static_cast<int>(advertiser.second.advertising_enable));
      return ErrorCode::COMMAND_DISALLOWED;
    }
  }

  extended_advertisers_.clear();
  return ErrorCode::SUCCESS;
}

uint16_t ExtendedAdvertiser::GetMaxAdvertisingDataLength(
    const AdvertisingEventProperties& properties) {
  // The PDU AdvData size is defined in the following sections:
  // - Vol 6, Part B 搂 2.3.1.1 ADV_IND
  // - Vol 6, Part B 搂 2.3.1.2 ADV_DIRECT_IND
  // - Vol 6, Part B 搂 2.3.1.3 ADV_NONCONN_IND
  // - Vol 6, Part B 搂 2.3.1.4 ADV_SCAN_IND
  // - Vol 6, Part B 搂 2.3.1.5 ADV_EXT_IND
  // - Vol 6, Part B 搂 2.3.1.6 AUX_ADV_IND
  // - Vol 6, Part B 搂 2.3.1.8 AUX_CHAIN_IND
  // - Vol 6, Part B 搂 2.3.4 Common Extended Advertising Payload Format
  uint16_t max_advertising_data_length;

  if (properties.legacy_ && properties.directed_) {
    // Directed legacy advertising PDUs do not have AdvData payload.
    max_advertising_data_length = 0;
  } else if (properties.legacy_) {
    max_advertising_data_length = max_legacy_advertising_pdu_size;
  } else if (properties.scannable_) {
    // Scannable extended advertising PDUs do not have AdvData payload.
    max_advertising_data_length = 0;
  } else if (!properties.connectable_) {
    // When extended advertising is non-scannable and non-connectable,
    // AUX_CHAIN_IND PDUs can be used, and the advertising data may be
    // fragmented over multiple PDUs; the length is still capped at 1650
    // as stated in Vol 6, Part B 搂 2.3.4.9 Host Advertising Data.
    max_advertising_data_length = max_extended_advertising_pdu_size;
  } else {
    // When extended advertising is either scannable or connectable,
    // AUX_CHAIN_IND PDUs may not be used, and the maximum advertising data
    // length is 254. Extended payload header fields eat into the
    // available space.
    max_advertising_data_length = 254;
    max_advertising_data_length -= 6;                         // AdvA
    max_advertising_data_length -= 2;                         // ADI
    max_advertising_data_length -= 6 * properties.directed_;  // TargetA
    max_advertising_data_length -= 1 * properties.tx_power_;  // TxPower
    // TODO(pedantic): configure the ACAD field in order to leave the least
    // amount of AdvData space to the user (191).
  }

  return max_advertising_data_length;
}

uint16_t ExtendedAdvertiser::GetMaxScanResponseDataLength(
    const AdvertisingEventProperties& properties) {
  // The PDU AdvData size is defined in the following sections:
  // - Vol 6, Part B 搂 2.3.2.2 SCAN_RSP
  // - Vol 6, Part B 搂 2.3.2.3 AUX_SCAN_RSP
  // - Vol 6, Part B 搂 2.3.1.8 AUX_CHAIN_IND
  // - Vol 6, Part B 搂 2.3.4 Common Extended Advertising Payload Format
  uint16_t max_scan_response_data_length;

  if (!properties.scannable_) {
    max_scan_response_data_length = 0;
  } else if (properties.legacy_) {
    max_scan_response_data_length = max_legacy_advertising_pdu_size;
  } else {
    // Extended scan response data may be sent over AUX_CHAIN_PDUs, and
    // the advertising data may be fragmented over multiple PDUs; the length
    // is still capped at 1650 as stated in
    // Vol 6, Part B 搂 2.3.4.9 Host Advertising Data.
    max_scan_response_data_length = max_extended_advertising_pdu_size;
  }

  return max_scan_response_data_length;
}

uint16_t ExtendedAdvertiser::GetRawAdvertisingEventProperties(
    const AdvertisingEventProperties& properties) {
  uint16_t mask = 0;
  if (properties.connectable_) {
    mask |= 0x1;
  }
  if (properties.scannable_) {
    mask |= 0x2;
  }
  if (properties.directed_) {
    mask |= 0x4;
  }
  if (properties.high_duty_cycle_) {
    mask |= 0x8;
  }
  if (properties.legacy_) {
    mask |= 0x10;
  }
  if (properties.anonymous_) {
    mask |= 0x20;
  }
  if (properties.tx_power_) {
    mask |= 0x40;
  }
  return mask;
}

// =============================================================================
//  Periodic Advertising Commands
// =============================================================================

// HCI LE Set Periodic Advertising Parameters command (Vol 4, Part E 搂 7.8.61).
ErrorCode LinkLayerController::LeSetPeriodicAdvertisingParameters(
    uint8_t advertising_handle, uint16_t periodic_advertising_interval_min,
    uint16_t periodic_advertising_interval_max, bool /*include_tx_power*/) {
  // The Advertising_Handle parameter identifies the advertising set whose
  // periodic advertising parameters are being configured. If the corresponding
  // advertising set does not already exist, then the Controller shall return
  // the error code Unknown Advertising Identifier (0x42).
  // TODO(c++20) unordered_map<>::contains
  if (extended_advertisers_.count(advertising_handle) == 0) {
    INFO(id_, "no advertising set defined with handle {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
  }

  ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];

  // The Periodic_Advertising_Interval_Min parameter shall be less than or
  // equal to the Periodic_Advertising_Interval_Max parameter.
  if (periodic_advertising_interval_min < 0x6 ||
      periodic_advertising_interval_max < 0x6 ||
      periodic_advertising_interval_max < periodic_advertising_interval_min) {
    INFO(id_, "invalid periodic advertising interval range {:04x} - {:04x}",
         periodic_advertising_interval_min, periodic_advertising_interval_max);
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the advertising set identified by the Advertising_Handle specified
  // scannable, connectable, legacy, or anonymous advertising, the Controller
  // shall return the error code Invalid HCI Command Parameters (0x12).
  if (advertiser.advertising_event_properties.connectable_ ||
      advertiser.advertising_event_properties.scannable_ ||
      advertiser.advertising_event_properties.legacy_ ||
      advertiser.advertising_event_properties.anonymous_) {
    INFO(id_,
         "the periodic advertising set {:02x} specifies scannable,"
         " connectable, legacy or anonymous advertising",
         static_cast<int>(advertising_handle));
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the Host issues this command when periodic advertising is enabled for
  // the specified advertising set, the Controller shall return the error code
  // Command Disallowed (0x0C).
  if (advertiser.periodic_advertising_enable) {
    INFO(id_, "periodic advertising is enabled for the set {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If the Advertising_Handle does not identify an advertising set that is
  // already configured for periodic advertising and the Controller is unable
  // to support more periodic advertising at present, the Controller shall
  // return the error code Memory Capacity Exceeded (0x07)
  // TODO: add controller configuration for maximum number of periodic
  // advertising sets.

  // If the advertising set already contains periodic advertising data and the
  // length of the data is greater than the maximum that the Controller can
  // transmit within a periodic advertising interval of
  // Periodic_Advertising_Interval_Max, the Controller shall return the error
  // code Packet Too Long (0x45).
  if (advertiser.periodic_advertising_data.size() >
      ExtendedAdvertiser::GetMaxPeriodicAdvertisingDataLength(
          slots(periodic_advertising_interval_max))) {
    INFO(id_,
         "the length of the periodic advertising data exceeds the maximum"
         " that the controller can transmit within the maximum periodic"
         " advertising interval");
    return ErrorCode::PACKET_TOO_LONG;
  }

  advertiser.periodic_advertising_interval =
      slots(periodic_advertising_interval_max);
  return ErrorCode::SUCCESS;
}

// HCI LE Set Periodic Advertising Data command (Vol 4, Part E 搂 7.8.62).
ErrorCode LinkLayerController::LeSetPeriodicAdvertisingData(
    uint8_t advertising_handle, bluetooth::hci::Operation operation,
    const std::vector<uint8_t>& advertising_data) {
  // If the advertising set corresponding to the Advertising_Handle parameter
  // does not exist, then the Controller shall return the error code
  // Unknown Advertising Identifier (0x42).
  // TODO(c++20) unordered_map<>::contains
  if (extended_advertisers_.count(advertising_handle) == 0) {
    INFO(id_, "no advertising set defined with handle {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
  }

  ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];

  // If the advertising set has not been configured for periodic advertising,
  // then the Controller shall return the error code Command Disallowed (0x0C).
  if (advertiser.periodic_advertising_interval.count() == 0) {
    INFO(id_, "periodic advertising is not configured for the set {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If periodic advertising is currently enabled for the specified advertising
  // set and Operation does not have the value 0x03 or 0x04, then the Controller
  // shall return the error code Command Disallowed (0x0C).
  if (advertiser.periodic_advertising_enable &&
      operation != Operation::COMPLETE_ADVERTISEMENT &&
      operation != Operation::UNCHANGED_DATA) {
    INFO(id_,
         "periodic advertising is enabled and the operation is not"
         " Complete_Advertisement or Unchanged_Data");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If Operation is not 0x03 or 0x04 and Advertising_Data_Length is zero,
  // then the Controller shall return the error code
  // Invalid HCI Command Parameters (0x12).
  if (advertising_data.empty() &&
      operation != Operation::COMPLETE_ADVERTISEMENT &&
      operation != Operation::UNCHANGED_DATA) {
    INFO(id_,
         "periodic advertising data is empty is enabled and the operation"
         " is not Complete_Advertisement or Unchanged_Data");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If Operation is 0x04 and:
  // 鈥� periodic advertising is currently disabled for the advertising set;
  // 鈥� the periodic advertising set contains no data; or
  // 鈥� Advertising_Data_Length is not zero;
  // then the Controller shall return the error code
  // Invalid HCI Command Parameters (0x12).
  if (operation == Operation::UNCHANGED_DATA &&
      (!advertiser.periodic_advertising_enable ||
       advertiser.periodic_advertising_data.empty() ||
       !advertising_data.empty())) {
    INFO(
        id_,
        "Unchanged_Data operation is used but periodic advertising is disabled;"
        " or the periodic advertising set contains no data;"
        " or the advertising data is not empty");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  switch (operation) {
    case Operation::INTERMEDIATE_FRAGMENT:
      advertiser.periodic_advertising_data.insert(
          advertiser.periodic_advertising_data.end(), advertising_data.begin(),
          advertising_data.end());
      advertiser.partial_periodic_advertising_data = true;
      break;

    case Operation::FIRST_FRAGMENT:
      advertiser.periodic_advertising_data = advertising_data;
      advertiser.partial_periodic_advertising_data = true;
      break;

    case Operation::LAST_FRAGMENT:
      advertiser.periodic_advertising_data.insert(
          advertiser.periodic_advertising_data.end(), advertising_data.begin(),
          advertising_data.end());
      advertiser.partial_periodic_advertising_data = false;
      break;

    case Operation::COMPLETE_ADVERTISEMENT:
      advertiser.periodic_advertising_data = advertising_data;
      advertiser.partial_periodic_advertising_data = false;
      break;

    case Operation::UNCHANGED_DATA:
      break;

    default:
      INFO(id_, "unknown operation ({})", static_cast<int>(operation));
      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the combined length of the data exceeds the capacity of the advertising
  // set identified by the Advertising_Handle parameter or the amount of memory
  // currently available, all the data shall be discarded and the Controller
  // shall return the error code Memory Capacity Exceeded (0x07).
  if (advertiser.periodic_advertising_data.size() >
      properties_.le_max_advertising_data_length) {
    INFO(id_,
         "the length of the combined periodic advertising data exceeds"
         " the maximum advertising data length");
    advertiser.periodic_advertising_data.clear();
    advertiser.partial_periodic_advertising_data = false;
    return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
  }

  // If the combined length of the data is greater than the maximum that the
  // Controller can transmit within the current periodic advertising interval
  // for the advertising set, all the data shall be discarded and the
  // Controller shall return the error code Packet Too Long (0x45).
  if (advertiser.periodic_advertising_data.size() >
      ExtendedAdvertiser::GetMaxPeriodicAdvertisingDataLength(
          advertiser.periodic_advertising_interval)) {
    INFO(id_,
         "the length of the combined periodic advertising data exceeds"
         " the maximum that the controller can transmit within the current"
         " periodic advertising interval");
    advertiser.periodic_advertising_data.clear();
    advertiser.partial_periodic_advertising_data = false;
    return ErrorCode::PACKET_TOO_LONG;
  }

  return ErrorCode::SUCCESS;
}

// HCI LE Set Periodic Advertising Enable command (Vol 4, Part E 搂 7.8.63).
ErrorCode LinkLayerController::LeSetPeriodicAdvertisingEnable(
    bool enable, bool include_adi, uint8_t advertising_handle) {
  // If the advertising set corresponding to the Advertising_Handle parameter
  // does not exist, the Controller shall return the error code Unknown
  // Advertising Identifier (0x42).
  // TODO(c++20) unordered_map<>::contains
  if (extended_advertisers_.count(advertising_handle) == 0) {
    INFO(id_, "no advertising set defined with handle {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
  }

  ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];

  if (!enable) {
    advertiser.DisablePeriodic();
    return ErrorCode::SUCCESS;
  }

  // If bit 0 of Enable is set to 1 (periodic advertising is enabled) and the
  // advertising set contains partial periodic advertising data, the Controller
  // shall return the error code Command Disallowed (0x0C).
  if (advertiser.partial_periodic_advertising_data) {
    INFO(id_, "the advertising set contains partial periodic advertising data");
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If bit 0 of Enable is set to 1 and the Host has not issued the
  // HCI_LE_Set_Periodic_Advertising_Parameters command for the advertising set,
  // the Controller shall either use vendor-specified parameters or return the
  // error code Command Disallowed (0x0C).
  if (advertiser.periodic_advertising_interval.count() == 0) {
    INFO(id_, "periodic advertising is not configured for the set {:02x}",
         static_cast<int>(advertising_handle));
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If bit 0 of Enable is set to 1 and the length of the periodic advertising
  // data is greater than the maximum that the Controller can transmit within
  // the chosen periodic advertising interval, the Controller shall return the
  // error code Packet Too Long (0x45).
  if (advertiser.periodic_advertising_data.size() >
      ExtendedAdvertiser::GetMaxPeriodicAdvertisingDataLength(
          advertiser.periodic_advertising_interval)) {
    INFO(id_,
         "the length of the combined periodic advertising data exceeds"
         " the maximum that the controller can transmit within the current"
         " periodic advertising interval");
    return ErrorCode::PACKET_TOO_LONG;
  }

  // If bit 0 of Enable is set to 1 and the advertising set identified by the
  // Advertising_Handle specified scannable, connectable, legacy, or anonymous
  // advertising, the Controller shall return the error code
  // Command Disallowed (0x0C).
  if (advertiser.advertising_event_properties.connectable_ ||
      advertiser.advertising_event_properties.scannable_ ||
      advertiser.advertising_event_properties.legacy_ ||
      advertiser.advertising_event_properties.anonymous_) {
    INFO(id_,
         "the periodic advertising set {:02x} specifies scannable,"
         " connectable, legacy or anonymous advertising",
         static_cast<int>(advertising_handle));
    return ErrorCode::COMMAND_DISALLOWED;
  }

  // If bit 1 of Enable is set to 1 and the Controller does not support the
  // Periodic Advertising ADI Support feature, the Controller shall return an
  // error which should use the error code Unsupported Feature or
  // Parameter Value (0x11).
  if (include_adi && !properties_.SupportsLLFeature(
                         LLFeaturesBits::PERIODIC_ADVERTISING_ADI_SUPPORT)) {
    INFO(id_,
         "include ADI is true but the controller does not support the"
         " Periodic Advertising ADI Supported feature",
         static_cast<int>(advertising_handle));
    return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
  }

  advertiser.EnablePeriodic();
  return ErrorCode::SUCCESS;
}

uint16_t ExtendedAdvertiser::GetMaxPeriodicAdvertisingDataLength(
    slots /*periodic_advertising_interval*/) {
  // TODO: evaluate the maximum length of the advertising PDU that can
  // be physically sent in the advertising interval.
  return max_extended_advertising_pdu_size;
}

// =============================================================================
//  Advertising Routines
// =============================================================================

void LinkLayerController::LeAdvertising() {
  chrono::time_point now = std::chrono::steady_clock::now();

  // Legacy Advertising Timeout

  // Generate HCI Connection Complete or Enhanced HCI Connection Complete
  // events with Advertising Timeout error code when the advertising
  // type is ADV_DIRECT_IND and the connection failed to be established.
  if (legacy_advertiser_.IsEnabled() && legacy_advertiser_.timeout &&
      now >= legacy_advertiser_.timeout.value()) {
    // If the Advertising_Type parameter is 0x01 (ADV_DIRECT_IND, high duty
    // cycle) and the directed advertising fails to create a connection, an
    // HCI_LE_Connection_Complete or HCI_LE_Enhanced_Connection_Complete
    // event shall be generated with the Status code set to
    // Advertising Timeout (0x3C).
    INFO(id_, "Directed Advertising Timeout");
    legacy_advertiser_.Disable();

    // TODO: The PTS tool expects an LE_Connection_Complete event in this
    // case and will fail the test GAP/DISC/GENP/BV-05-C if
    // LE_Enhanced_Connection_Complete is sent instead.
    //
    // Note: HCI_LE_Connection_Complete is not sent if the
    // HCI_LE_Enhanced_Connection_Complete event (see Section 7.7.65.10)
    // is unmasked.
#if 0
    if (IsLeEventUnmasked(SubeventCode::ENHANCED_CONNECTION_COMPLETE)) {
      send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create(
          ErrorCode::ADVERTISING_TIMEOUT, 0, Role::CENTRAL,
          AddressType::PUBLIC_DEVICE_ADDRESS, Address(), Address(), Address(),
          0, 0, 0, ClockAccuracy::PPM_500));
    } else
#endif
    if (IsLeEventUnmasked(SubeventCode::CONNECTION_COMPLETE)) {
      send_event_(bluetooth::hci::LeConnectionCompleteBuilder::Create(
          ErrorCode::ADVERTISING_TIMEOUT, 0, Role::CENTRAL,
          AddressType::PUBLIC_DEVICE_ADDRESS, Address(), 0, 0, 0,
          ClockAccuracy::PPM_500));
    }
  }

  // Legacy Advertising Event

  // Generate Link Layer Advertising events when advertising is enabled
  // and a full interval has passed since the last event.
  if (legacy_advertiser_.IsEnabled() && now >= legacy_advertiser_.next_event) {
    legacy_advertiser_.next_event =
        now + legacy_advertiser_.advertising_interval;
    model::packets::LegacyAdvertisingType type;
    bool attach_advertising_data = true;
    switch (legacy_advertiser_.advertising_type) {
      case AdvertisingType::ADV_IND:
        type = model::packets::LegacyAdvertisingType::ADV_IND;
        break;
      case AdvertisingType::ADV_DIRECT_IND_HIGH:
      case AdvertisingType::ADV_DIRECT_IND_LOW:
        attach_advertising_data = false;
        type = model::packets::LegacyAdvertisingType::ADV_DIRECT_IND;
        break;
      case AdvertisingType::ADV_SCAN_IND:
        type = model::packets::LegacyAdvertisingType::ADV_SCAN_IND;
        break;
      case AdvertisingType::ADV_NONCONN_IND:
        type = model::packets::LegacyAdvertisingType::ADV_NONCONN_IND;
        break;
    }

    SendLeLinkLayerPacket(
        model::packets::LeLegacyAdvertisingPduBuilder::Create(
            legacy_advertiser_.advertising_address.GetAddress(),
            legacy_advertiser_.target_address.GetAddress(),
            static_cast<model::packets::AddressType>(
                legacy_advertiser_.advertising_address.GetAddressType()),
            static_cast<model::packets::AddressType>(
                legacy_advertiser_.target_address.GetAddressType()),
            type,
            attach_advertising_data ? legacy_advertiser_.advertising_data
                                    : std::vector<uint8_t>{}),
        properties_.le_advertising_physical_channel_tx_power);
  }

  for (auto& [_, advertiser] : extended_advertisers_) {
    // Extended Advertising Timeouts

    if (advertiser.IsEnabled() && advertiser.timeout.has_value() &&
        now >= advertiser.timeout.value()) {
      // If the Duration[i] parameter is set to a value other than 0x0000, an
      // HCI_LE_Advertising_Set_Terminated event shall be generated when the
      // duration specified in the Duration[i] parameter expires.
      // However, if the advertising set is for high duty cycle connectable
      // directed advertising and no connection is created before the duration
      // expires, an HCI_LE_Connection_Complete or
      // HCI_LE_Enhanced_Connection_Complete event with the Status parameter
      // set to the error code Advertising Timeout (0x3C) may be generated
      // instead of or in addition to the HCI_LE_Advertising_Set_Terminated
      // event.
      INFO(id_, "Extended Advertising Timeout");
      advertiser.Disable();

      bool high_duty_cycle_connectable_directed_advertising =
          advertiser.advertising_event_properties.directed_ &&
          advertiser.advertising_event_properties.connectable_ &&
          advertiser.advertising_event_properties.high_duty_cycle_;

      // Note: HCI_LE_Connection_Complete is not sent if the
      // HCI_LE_Enhanced_Connection_Complete event (see Section 7.7.65.10)
      // is unmasked.
      if (high_duty_cycle_connectable_directed_advertising &&
          IsLeEventUnmasked(SubeventCode::ENHANCED_CONNECTION_COMPLETE)) {
        send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create(
            ErrorCode::ADVERTISING_TIMEOUT, 0, Role::CENTRAL,
            AddressType::PUBLIC_DEVICE_ADDRESS, Address(), Address(), Address(),
            0, 0, 0, ClockAccuracy::PPM_500));
      } else if (high_duty_cycle_connectable_directed_advertising &&
                 IsLeEventUnmasked(SubeventCode::CONNECTION_COMPLETE)) {
        send_event_(bluetooth::hci::LeConnectionCompleteBuilder::Create(
            ErrorCode::ADVERTISING_TIMEOUT, 0, Role::CENTRAL,
            AddressType::PUBLIC_DEVICE_ADDRESS, Address(), 0, 0, 0,
            ClockAccuracy::PPM_500));
      }

      if (IsLeEventUnmasked(SubeventCode::ADVERTISING_SET_TERMINATED)) {
        // The parameter Num_Completed_Extended_Advertising_Events is set
        // only when Max_Extended_Advertising_Events was configured as
        // non-zero in the advertising parameters.
        uint8_t num_completed_extended_advertising_events =
            advertiser.max_extended_advertising_events != 0
                ? advertiser.num_completed_extended_advertising_events
                : 0;
        send_event_(bluetooth::hci::LeAdvertisingSetTerminatedBuilder::Create(
            ErrorCode::ADVERTISING_TIMEOUT, advertiser.advertising_handle, 0,
            num_completed_extended_advertising_events));
      }
    }

    if (advertiser.IsEnabled() && advertiser.max_extended_advertising_events &&
        advertiser.num_completed_extended_advertising_events >=
            advertiser.max_extended_advertising_events) {
      // If the Max_Extended_Advertising_Events[i] parameter is set to a value
      // other than 0x00, an HCI_LE_Advertising_Set_Terminated event shall be
      // generated when the maximum number of extended advertising events has
      // been transmitted by the Controller.
      INFO(id_, "Max Extended Advertising count reached");
      advertiser.Disable();

      if (IsLeEventUnmasked(SubeventCode::ADVERTISING_SET_TERMINATED)) {
        send_event_(bluetooth::hci::LeAdvertisingSetTerminatedBuilder::Create(
            ErrorCode::ADVERTISING_TIMEOUT, advertiser.advertising_handle, 0,
            advertiser.num_completed_extended_advertising_events));
      }
    }

    // Extended Advertising Event

    // Generate Link Layer Advertising events when advertising is enabled
    // and a full interval has passed since the last event.
    if (advertiser.IsEnabled() && now >= advertiser.next_event) {
      advertiser.next_event += advertiser.primary_advertising_interval;
      advertiser.num_completed_extended_advertising_events++;

      if (advertiser.advertising_event_properties.legacy_) {
        model::packets::LegacyAdvertisingType type;
        uint16_t raw_advertising_event_properties =
            ExtendedAdvertiser::GetRawAdvertisingEventProperties(
                advertiser.advertising_event_properties);
        switch (static_cast<LegacyAdvertisingEventProperties>(
            raw_advertising_event_properties & 0xf)) {
          case LegacyAdvertisingEventProperties::ADV_IND:
            type = model::packets::LegacyAdvertisingType::ADV_IND;
            break;
          case LegacyAdvertisingEventProperties::ADV_DIRECT_IND_HIGH:
          case LegacyAdvertisingEventProperties::ADV_DIRECT_IND_LOW:
            type = model::packets::LegacyAdvertisingType::ADV_DIRECT_IND;
            break;
          case LegacyAdvertisingEventProperties::ADV_SCAN_IND:
            type = model::packets::LegacyAdvertisingType::ADV_SCAN_IND;
            break;
          case LegacyAdvertisingEventProperties::ADV_NONCONN_IND:
            type = model::packets::LegacyAdvertisingType::ADV_NONCONN_IND;
            break;
          default:
            FATAL(
                id_,
                "unexpected raw advertising event properties;"
                " please check the extended advertising parameter validation");
            break;
        }

        SendLeLinkLayerPacket(
            model::packets::LeLegacyAdvertisingPduBuilder::Create(
                advertiser.advertising_address.GetAddress(),
                advertiser.target_address.GetAddress(),
                static_cast<model::packets::AddressType>(
                    advertiser.advertising_address.GetAddressType()),
                static_cast<model::packets::AddressType>(
                    advertiser.target_address.GetAddressType()),
                type, advertiser.advertising_data),
            advertiser.advertising_tx_power);
      } else {
        SendLeLinkLayerPacket(
            model::packets::LeExtendedAdvertisingPduBuilder::Create(
                advertiser.advertising_address.GetAddress(),
                advertiser.target_address.GetAddress(),
                static_cast<model::packets::AddressType>(
                    advertiser.advertising_address.GetAddressType()),
                static_cast<model::packets::AddressType>(
                    advertiser.target_address.GetAddressType()),
                advertiser.advertising_event_properties.connectable_,
                advertiser.advertising_event_properties.scannable_,
                advertiser.advertising_event_properties.directed_,
                advertiser.advertising_sid, advertiser.advertising_tx_power,
                static_cast<model::packets::PhyType>(
                    advertiser.primary_advertising_phy),
                static_cast<model::packets::PhyType>(
                    advertiser.secondary_advertising_phy),
                advertiser.periodic_advertising_interval.count(),
                advertiser.advertising_data),
            advertiser.advertising_tx_power);
      }
    }

    // Periodic Advertising Event

    // Generate Link Layer Advertising events when advertising is enabled
    // and a full interval has passed since the last event.
    if (advertiser.IsPeriodicEnabled() &&
        now >= advertiser.next_periodic_event) {
      advertiser.next_periodic_event +=
          advertiser.periodic_advertising_interval;
      SendLeLinkLayerPacket(
          model::packets::LePeriodicAdvertisingPduBuilder::Create(
              advertiser.advertising_address.GetAddress(), Address(),
              static_cast<model::packets::AddressType>(
                  advertiser.advertising_address.GetAddressType()),
              advertiser.advertising_sid, advertiser.advertising_tx_power,
              advertiser.periodic_advertising_interval.count(),
              advertiser.periodic_advertising_data),
          advertiser.advertising_tx_power);
    }
  }
}

}  // namespace rootcanal