text: // Copyright 2020 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  // clang-format off
  #include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
  #include <Weave/DeviceLayer/internal/BLEManager.h>
  // clang-format on
  
  #include <lib/syslog/cpp/macros.h>
  
  #if WEAVE_DEVICE_CONFIG_ENABLE_WOBLE
  
  #define MAX_CHARACTERISTIC_UUID_SIZE 40
  
  using namespace ::nl;
  using namespace ::nl::Ble;
  
  namespace nl {
  namespace Weave {
  namespace DeviceLayer {
  namespace Internal {
  
  namespace {
  
  /// UUID of weave service obtained from SIG, in canonical 8-4-4-4-12 string format.
  constexpr char kServiceUuid[] = "0000FEAF-0000-1000-8000-00805F9B34FB";
  
  // Define structure that holds both the canonical and WeaveBLEUUID format.
  struct BLECharUUID {
    const char canonical_uuid[MAX_CHARACTERISTIC_UUID_SIZE];
    const WeaveBleUUID weave_uuid;
  };
  
  // Offsets into |kWeaveBleChars| for specific characteristic.
  enum WeaveBleChar {
    // Weave service characteristic C1(write)
    kWeaveBleCharWrite = 0,
    // Weave service characteristic C2(indicate)
    kWeaveBleCharIndicate = 1,
  };
  
  // An array that holds the UUID for each |WeaveBleChar|
  constexpr BLECharUUID kWeaveBleChars[] = {
      // UUID for |kWeaveBleCharWrite|
      {"18EE2EF5-263D-4559-959F-4F9C429F9D11",
       {{0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F, 0x9D,
         0x11}}},
      // UUID for |kWeaveBleCharIndicate|
      {"18EE2EF5-263D-4559-959F-4F9C429F9D12",
       {{0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F, 0x9D,
         0x12}}}};
  
  }  // unnamed namespace
  
  BLEManagerImpl BLEManagerImpl::sInstance;
  
  BLEManagerImpl::BLEManagerImpl() : woble_connection_(this), gatt_binding_(this) {
    // BleLayer does not initialize this callback, set it NULL to avoid accessing location pointed by
    // garbage value when not set explicitly.
    OnWeaveBleConnectReceived = NULL;
  }
  
  WEAVE_ERROR BLEManagerImpl::_Init() {
    WEAVE_ERROR err;
    auto svc = PlatformMgrImpl().GetComponentContextForProcess()->svc();
  
    FX_CHECK(svc->Connect(gatt_server_.NewRequest()) == ZX_OK)
        << "Failed to connect to " << fuchsia::bluetooth::gatt::Server::Name_;
    FX_CHECK(svc->Connect(peripheral_.NewRequest()) == ZX_OK)
        << "Failed to connect to " << fuchsia::bluetooth::le::Peripheral::Name_;
  
    adv_handle_.set_error_handler(
        [](zx_status_t status) { FX_LOGS(INFO) << "LE advertising was stopped: " << status; });
  
    if (ConfigurationMgrImpl().IsWoBLEEnabled()) {
      service_mode_ = ConnectivityManager::kWoBLEServiceMode_Enabled;
    }
  
    memset(device_name_, 0, sizeof(device_name_));
  
    flags_ = 0;
    if (ConfigurationMgrImpl().IsWoBLEAdvertisementEnabled()) {
      flags_ = kFlag_AdvertisingEnabled;
    }
  
    // Initialize the Weave BleLayer.
    err = BleLayer::Init(this, this, &SystemLayer);
    if (err != BLE_NO_ERROR) {
      FX_LOGS(ERROR) << "BLE Layer init failed: " << ErrorStr(err);
      return err;
    }
  
    PlatformMgr().ScheduleWork(DriveBLEState, reinterpret_cast<intptr_t>(this));
    return WEAVE_NO_ERROR;
  }
  
  WEAVE_ERROR BLEManagerImpl::_SetWoBLEServiceMode(WoBLEServiceMode service_mode) {
    if (service_mode == ConnectivityManager::kWoBLEServiceMode_NotSupported) {
      return WEAVE_ERROR_INVALID_ARGUMENT;
    }
    if (service_mode_ == ConnectivityManager::kWoBLEServiceMode_NotSupported) {
      return WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE;
    }
    if (service_mode != service_mode_) {
      service_mode_ = service_mode;
      PlatformMgr().ScheduleWork(DriveBLEState, reinterpret_cast<intptr_t>(this));
    }
    return WEAVE_NO_ERROR;
  }
  
  WEAVE_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool advertising_enable) {
    if (service_mode_ == ConnectivityManager::kWoBLEServiceMode_NotSupported) {
      return WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE;
    }
  
    if (GetFlag(flags_, kFlag_AdvertisingEnabled) != advertising_enable) {
      SetFlag(flags_, kFlag_AdvertisingEnabled, advertising_enable);
      PlatformMgr().ScheduleWork(DriveBLEState, reinterpret_cast<intptr_t>(this));
    }
    return WEAVE_NO_ERROR;
  }
  
  WEAVE_ERROR BLEManagerImpl::_SetFastAdvertisingEnabled(bool fast_advertising_enable) {
    if (service_mode_ == ConnectivityManager::kWoBLEServiceMode_NotSupported) {
      return WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE;
    }
    // TODO(fxb/52734): Enable bluetooth fast advertising when needed
    return WEAVE_NO_ERROR;
  }
  
  WEAVE_ERROR BLEManagerImpl::_GetDeviceName(char* device_name, size_t device_name_size) {
    if (strlen(device_name_) >= device_name_size) {
      return WEAVE_ERROR_BUFFER_TOO_SMALL;
    }
    strncpy(device_name, device_name_, device_name_size);
    return WEAVE_NO_ERROR;
  }
  
  WEAVE_ERROR BLEManagerImpl::_SetDeviceName(const char* device_name) {
    if (service_mode_ == ConnectivityManager::kWoBLEServiceMode_NotSupported) {
      return WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE;
    }
    if (device_name != NULL && device_name[0] != 0) {
      if (strlen(device_name) >= kMaxDeviceNameLength) {
        return WEAVE_ERROR_INVALID_ARGUMENT;
      }
      strcpy(device_name_, device_name);
      SetFlag(flags_, kFlag_UseCustomDeviceName);
    } else {
      device_name_[0] = 0;
      ClearFlag(flags_, kFlag_UseCustomDeviceName);
    }
    return WEAVE_NO_ERROR;
  }
  
  void BLEManagerImpl::_OnPlatformEvent(const WeaveDeviceEvent* event) {
    BLEManagerImpl* instance;
    WoBLEConState* connection_state;
    if (event == NULL) {
      // Ignore null weave device event.
      return;
    }
    switch (event->Type) {
      case DeviceEventType::kWoBLESubscribe:
        connection_state = static_cast<WoBLEConState*>(event->WoBLESubscribe.ConId);
        ZX_ASSERT_MSG(connection_state != NULL,
                      "Received WoBLE subscribe event without connection state");
        instance = connection_state->instance;
        ZX_ASSERT_MSG(instance != NULL, "Received WoBLE subscribe event with NULL instance");
        instance->HandleSubscribeReceived(
            event->WoBLESubscribe.ConId, &WEAVE_BLE_SVC_ID,
            &kWeaveBleChars[WeaveBleChar::kWeaveBleCharIndicate].weave_uuid);
  
        // Post a WoBLEConnectionEstablished event to the DeviceLayer
        WeaveDeviceEvent connection_established_event;
        connection_established_event.Type = DeviceEventType::kWoBLEConnectionEstablished;
        PlatformMgr().PostEvent(&connection_established_event);
  
        break;
  
      case DeviceEventType::kWoBLEUnsubscribe:
        connection_state = static_cast<WoBLEConState*>(event->WoBLEUnsubscribe.ConId);
        ZX_ASSERT_MSG(connection_state != NULL,
                      "Received WoBLE unsubscribe event without connection state");
        instance = connection_state->instance;
        ZX_ASSERT_MSG(instance != NULL, "Received WoBLE unsubscribe event with NULL instance");
        instance->HandleUnsubscribeReceived(
            event->WoBLEUnsubscribe.ConId, &WEAVE_BLE_SVC_ID,
            &kWeaveBleChars[WeaveBleChar::kWeaveBleCharIndicate].weave_uuid);
        break;
      case DeviceEventType::kWoBLEWriteReceived:
        connection_state = static_cast<WoBLEConState*>(event->WoBLEWriteReceived.ConId);
        ZX_ASSERT_MSG(connection_state != NULL,
                      "Received WoBLE write event without connection state");
        instance = connection_state->instance;
        ZX_ASSERT_MSG(instance != NULL, "Received WoBLE write event with NULL instance");
        instance->HandleWriteReceived(event->WoBLEWriteReceived.ConId, &WEAVE_BLE_SVC_ID,
                                      &kWeaveBleChars[WeaveBleChar::kWeaveBleCharWrite].weave_uuid,
                                      event->WoBLEWriteReceived.Data);
        break;
      case DeviceEventType::kWoBLEIndicateConfirm:
        connection_state = static_cast<WoBLEConState*>(event->WoBLEIndicateConfirm.ConId);
        ZX_ASSERT_MSG(connection_state != NULL,
                      "Received WoBLE indication confirmation event without connection state");
        instance = connection_state->instance;
        ZX_ASSERT_MSG(instance != NULL,
                      "Received WoBLE indication confirmation event with NULL instance");
        instance->HandleIndicationConfirmation(
            event->WoBLEIndicateConfirm.ConId, &WEAVE_BLE_SVC_ID,
            &kWeaveBleChars[WeaveBleChar::kWeaveBleCharIndicate].weave_uuid);
        break;
      case DeviceEventType::kWoBLEConnectionError:
        connection_state = static_cast<WoBLEConState*>(event->WoBLEConnectionError.ConId);
        ZX_ASSERT(connection_state != NULL);
        instance = connection_state->instance;
        ZX_ASSERT(instance != NULL);
        instance->HandleConnectionError(event->WoBLEConnectionError.ConId,
                                        event->WoBLEConnectionError.Reason);
        break;
      default:
        // Ignore events not intended for BLEManager.
        break;
    }
  }
  
  uint16_t BLEManagerImpl::_NumConnections(void) { return 0; }
  
  bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const WeaveBleUUID* svcId,
                                               const WeaveBleUUID* charId) {
    return false;
  }
  
  bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId,
                                                 const WeaveBleUUID* svcId,
                                                 const WeaveBleUUID* charId) {
    return false;
  }
  
  bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) { return false; }
  
  uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const { return 0; }
  
  bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const WeaveBleUUID* svcId,
                                      const WeaveBleUUID* charId, PacketBuffer* data) {
    auto connection_state = static_cast<WoBLEConState*>(conId);
    if (!connection_state) {
      PacketBuffer::Free(data);
      return false;
    }
  
    std::vector<uint8_t> value(data->DataLength());
    std::copy(data->Start(), data->Start() + data->DataLength(), value.begin());
  
    WEAVE_ERROR err =
        service_->NotifyValue(kWeaveBleCharIndicate, connection_state->peer_id, value, true);
    if (err != WEAVE_NO_ERROR) {
      FX_LOGS(ERROR) << "SendIndication failed: " << err;
      PacketBuffer::Free(data);
      return false;
    }
  
    // Save a reference to the buffer until we get a indication for the notification.
    connection_state->pending_ind_buf = data;
    data = NULL;
  
    // TODO(fxb/53070, fxb/53966): The peer confirmation currently isn't returned to the caller.
    // Proceed as if the confirmation is received, to avoid closing the connection. When the bug
    // is fixed, block until the confirmation is received and handle it.
    PacketBuffer::Free(connection_state->pending_ind_buf);
    // If the confirmation was successful...
    // Post an event to the Weave queue to process the indicate confirmation.
    WeaveDeviceEvent event;
    event.Type = DeviceEventType::kWoBLEIndicateConfirm;
    event.WoBLESubscribe.ConId = conId;
    PlatformMgr().PostEvent(&event);
  
    return true;
  }
  
  bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const WeaveBleUUID* svcId,
                                        const WeaveBleUUID* charId, PacketBuffer* pBuf) {
    return false;
  }
  
  bool BLEManagerImpl::SendReadRequest(BLE_CONNECTION_OBJECT conId, const WeaveBleUUID* svcId,
                                       const WeaveBleUUID* charId, PacketBuffer* pBuf) {
    return false;
  }
  
  bool BLEManagerImpl::SendReadResponse(BLE_CONNECTION_OBJECT conId,
                                        BLE_READ_REQUEST_CONTEXT requestContext,
                                        const WeaveBleUUID* svcId, const WeaveBleUUID* charId) {
    return false;
  }
  
  void BLEManagerImpl::NotifyWeaveConnectionClosed(BLE_CONNECTION_OBJECT conId) {}
  
  void BLEManagerImpl::DriveBLEState() {
    if (service_mode_ != ConnectivityManager::kWoBLEServiceMode_Enabled) {
      if (GetFlag(flags_, kFlag_Advertising)) {
        adv_handle_.Unbind();
        ClearFlag(flags_, kFlag_Advertising);
      }
      if (GetFlag(flags_, kFlag_GATTServicePublished)) {
        service_->RemoveService();
        ClearFlag(flags_, kFlag_GATTServicePublished);
      }
      return;
    }
  
    if (!GetFlag(flags_, kFlag_GATTServicePublished)) {
      fuchsia::bluetooth::Status out_status;
      fuchsia::bluetooth::gatt::ServiceInfo gatt_service_info;
  
      fuchsia::bluetooth::gatt::Characteristic weave_characteristic_c1;
      weave_characteristic_c1.id = WeaveBleChar::kWeaveBleCharWrite;
      weave_characteristic_c1.type = kWeaveBleChars[WeaveBleChar::kWeaveBleCharWrite].canonical_uuid;
      weave_characteristic_c1.properties = fuchsia::bluetooth::gatt::kPropertyWrite;
      weave_characteristic_c1.permissions = fuchsia::bluetooth::gatt::AttributePermissions::New();
      weave_characteristic_c1.permissions->write =
          fuchsia::bluetooth::gatt::SecurityRequirements::New();
  
      fuchsia::bluetooth::gatt::Characteristic weave_characteristic_c2;
      weave_characteristic_c2.id = WeaveBleChar::kWeaveBleCharIndicate;
      weave_characteristic_c2.type =
          kWeaveBleChars[WeaveBleChar::kWeaveBleCharIndicate].canonical_uuid;
      weave_characteristic_c2.properties =
          fuchsia::bluetooth::gatt::kPropertyRead | fuchsia::bluetooth::gatt::kPropertyIndicate;
      weave_characteristic_c2.permissions = fuchsia::bluetooth::gatt::AttributePermissions::New();
      weave_characteristic_c2.permissions->read =
          fuchsia::bluetooth::gatt::SecurityRequirements::New();
      weave_characteristic_c2.permissions->update =
          fuchsia::bluetooth::gatt::SecurityRequirements::New();
  
      std::vector<fuchsia::bluetooth::gatt::Characteristic> characteristics;
      characteristics.push_back(std::move(weave_characteristic_c1));
      characteristics.push_back(std::move(weave_characteristic_c2));
  
      gatt_service_info.primary = true;
      gatt_service_info.type = kServiceUuid;
      gatt_service_info.characteristics = std::move(characteristics);
      gatt_server_->PublishService(std::move(gatt_service_info), gatt_binding_.NewBinding(),
                                   service_.NewRequest(), &out_status);
      if (out_status.error) {
        FX_LOGS(ERROR) << "Failed to publish GATT service for Weave. Error: "
                       << (bool)out_status.error->error_code << " (" << out_status.error->description
                       << "). Disabling WoBLE service";
        service_mode_ = ConnectivityManager::kWoBLEServiceMode_Disabled;
        return;
      }
  
      FX_LOGS(INFO) << "Published GATT service for Weave with UUID: " << kServiceUuid;
      SetFlag(flags_, kFlag_GATTServicePublished);
    }
  
    if (GetFlag(flags_, kFlag_AdvertisingEnabled) && !GetFlag(flags_, kFlag_Advertising)) {
      fuchsia::bluetooth::le::AdvertisingData advertising_data;
      fuchsia::bluetooth::le::AdvertisingParameters advertising_parameters;
      fuchsia::bluetooth::le::Peripheral_StartAdvertising_Result advertising_result;
      fuchsia::bluetooth::Uuid uuid;
      std::copy(std::rbegin(WEAVE_BLE_SVC_ID.bytes), std::rend(WEAVE_BLE_SVC_ID.bytes),
                std::begin(uuid.value));
  
      // If a custom device name has not been specified, generate a name based on the
      // configured prefix and bottom digits of the Weave device id.
      if (!GetFlag(flags_, kFlag_UseCustomDeviceName)) {
        size_t out_len;
        char device_name_prefix[kMaxDeviceNameLength - 3] = "";
        WEAVE_ERROR err = ConfigurationMgrImpl().GetBleDeviceNamePrefix(
            device_name_prefix, kMaxDeviceNameLength - 4, &out_len);
        if (err != WEAVE_NO_ERROR) {
          FX_LOGS(ERROR) << "Failed to get BLE device name prefix: " << err;
        }
        snprintf(device_name_, sizeof(device_name_), "%s%04" PRIX32, device_name_prefix,
                 (uint32_t)FabricState.LocalNodeId);
        device_name_[kMaxDeviceNameLength] = 0;
      }
      advertising_data.set_name(device_name_);
      advertising_data.set_service_uuids({{uuid}});
  
      advertising_parameters.set_connectable(true);
      advertising_parameters.set_data(std::move(advertising_data));
      advertising_parameters.set_mode_hint(fuchsia::bluetooth::le::AdvertisingModeHint::SLOW);
  
      peripheral_->StartAdvertising(std::move(advertising_parameters), adv_handle_.NewRequest(),
                                    &advertising_result);
  
      if (advertising_result.is_err()) {
        FX_LOGS(ERROR) << "Failed to advertise WoBLE service, Error: "
                       << (uint32_t)advertising_result.err() << ". Disabling WoBLE service";
        service_mode_ = ConnectivityManager::kWoBLEServiceMode_Disabled;
        return;
      }
      FX_LOGS(INFO) << "Advertising Weave service for device: " << device_name_;
      SetFlag(flags_, kFlag_Advertising);
    }
  
    if (!GetFlag(flags_, kFlag_AdvertisingEnabled) && GetFlag(flags_, kFlag_Advertising)) {
      adv_handle_.Unbind();
      ClearFlag(flags_, kFlag_Advertising);
    }
  }
  
  void BLEManagerImpl::DriveBLEState(intptr_t arg) {
    auto instance = reinterpret_cast<BLEManagerImpl*>(arg);
    if (!instance) {
      FX_LOGS(ERROR) << "DriveBLEState called with NULL";
      return;
    }
    instance->DriveBLEState();
  }
  
  void BLEManagerImpl::OnCharacteristicConfiguration(uint64_t characteristic_id, std::string peer_id,
                                                     bool notify, bool indicate) {
    FX_LOGS(INFO) << "CharacteristicConfiguration on peer " << peer_id << " (notify: " << notify
                  << ", indicate: " << indicate << ") characteristic id " << characteristic_id;
  
    // Post an event to the Weave queue to process either a WoBLE Subscribe or Unsubscribe based on
    // whether the client is enabling or disabling indications.
    WeaveDeviceEvent event;
    event.Type = (indicate) ? DeviceEventType::kWoBLESubscribe : DeviceEventType::kWoBLEUnsubscribe;
    woble_connection_.peer_id = peer_id;
    event.WoBLESubscribe.ConId = static_cast<void*>(&woble_connection_);
    PlatformMgr().PostEvent(&event);
  }
  
  void BLEManagerImpl::OnReadValue(uint64_t id, int32_t offset, OnReadValueCallback callback) {
    callback({}, fuchsia::bluetooth::gatt::ErrorCode::NOT_PERMITTED);
  }
  
  void BLEManagerImpl::OnWriteValue(uint64_t id, uint16_t offset, std::vector<uint8_t> value,
                                    OnWriteValueCallback callback) {
    PacketBuffer* buf = NULL;
    FX_LOGS(INFO) << "Write request received for characteristic id " << id << " at offset " << offset
                  << " length " << value.size();
    if (id != kWeaveBleCharWrite) {
      FX_LOGS(WARNING) << "Ignoring writes to characteristic other than weave characteristic "
                          "C1(write). Expected characteristic: "
                       << kWeaveBleCharWrite;
      callback(fuchsia::bluetooth::gatt::ErrorCode::NOT_PERMITTED);
      return;
    }
    if (offset != 0) {
      FX_LOGS(ERROR) << "No offset expected on write to control point";
      callback(fuchsia::bluetooth::gatt::ErrorCode::INVALID_OFFSET);
      return;
    }
  
    // Copy the data to a PacketBuffer.
    buf = PacketBuffer::New(0);
    if (!buf || buf->AvailableDataLength() < value.size()) {
      FX_LOGS(ERROR) << "Buffer not available";
      callback(fuchsia::bluetooth::gatt::ErrorCode::INVALID_VALUE_LENGTH);
      PacketBuffer::Free(buf);
      return;
    }
  
    std::copy(value.begin(), value.end(), buf->Start());
    buf->SetDataLength(value.size());
  
    // Post an event to the Weave queue to deliver the data into the Weave stack.
    WeaveDeviceEvent event;
    event.Type = DeviceEventType::kWoBLEWriteReceived;
    event.WoBLEWriteReceived.ConId = static_cast<void*>(&woble_connection_);
    event.WoBLEWriteReceived.Data = buf;
    PlatformMgr().PostEvent(&event);
    buf = NULL;
  
    callback(fuchsia::bluetooth::gatt::ErrorCode::NO_ERROR);
  }
  
  void BLEManagerImpl::OnWriteWithoutResponse(uint64_t id, uint16_t offset,
                                              std::vector<uint8_t> value) {}
  
  }  // namespace Internal
  }  // namespace DeviceLayer
  }  // namespace Weave
  }  // namespace nl
  
  #endif  // WEAVE_DEVICE_CONFIG_ENABLE_WOBLE
  