Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d2776ec authored by Martin Brabham's avatar Martin Brabham
Browse files

Device: Initial bluetooth device API

Design:
    Have a simple memory map of devices for CLASSIC, LE, and DUAL.
This map will be managed by the DeviceDatabase and will eventually (not in this CL)
synchronize(using not yet submitted DeviceStorageApi) bonded devices to disk.

Bug: 139135297
Test: atest --host -t bluetooth_test_gd:DeviceTest bluetooth_test_gd:DeviceDatabaseTest bluetooth_test_gd:DualDeviceTest
Change-Id: I823b747aec5073feba5dc0f192f744329b43faf8
parent 249092c6
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@ filegroup {
        "controller.cc",
        "address.cc",
        "class_of_device.cc",
        "device.cc",
        "device_database.cc",
        "hci_layer.cc",
    ],
}
@@ -19,6 +21,9 @@ filegroup {
        "controller_test.cc",
        "address_unittest.cc",
        "class_of_device_unittest.cc",
        "device_test.cc",
        "device_database_test.cc",
        "dual_device_test.cc",
        "hci_layer_test.cc",
    ],
}
+35 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2019 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.
 *
 ******************************************************************************/
#pragma once

#include "hci/device.h"

namespace bluetooth::hci {

/**
 * A device representing a CLASSIC device.
 *
 * <p>This can be a CLASSIC only or a piece of a DUAL MODE device.
 */
class ClassicDevice : public Device {
 protected:
  friend class DeviceDatabase;
  explicit ClassicDevice(Address address) : Device(address, DeviceType::CLASSIC) {}
};

}  // namespace bluetooth::hci
+32 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2019 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 "hci/device.h"

using namespace bluetooth::hci;

std::string Device::generate_uid() {
  // TODO(optedoblivion): Figure out a good way to do this for what we want
  // to do
  // TODO(optedoblivion): Need to make a way to override something for LE pub addr case
  // Not sure if something like this is needed, but here is the idea (I think it came from mylesgw)
  // CLASSIC: have all 0s in front for classic then have private address
  // LE: have first public address in front then all 0s
  // LE: have first public address in front then private address
  //
  return address_.ToString();
}

system/gd/hci/device.h

0 → 100644
+136 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2019 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.
 *
 ******************************************************************************/
#pragma once

#include <string>

#include "hci/address.h"
#include "hci/class_of_device.h"

namespace bluetooth::hci {

/**
 * Used to determine device functionality
 */
enum DeviceType { DUAL, CLASSIC, LE };

/**
 * Represents a physical HCI device.
 *
 * <p>Contains all of the metadata required to represent a phycial device.
 *
 * <p>Devices should only be created and modified by HCI.
 */
class Device {
 public:
  virtual ~Device() = default;

  Address GetAddress() const {
    return address_;
  }

  /**
   * Returns 1 of 3 enum values for device's type (DUAL, CLASSIC, LE)
   */
  DeviceType GetDeviceType() const {
    return device_type_;
  }

  /**
   * Unique identifier for bluetooth devices
   *
   * @return string representation of the uuid
   */
  std::string /** use UUID when ported */ GetUuid() {
    return uid_;
  }

  std::string GetName() {
    return name_;
  }

  ClassOfDevice GetClassOfDevice() {
    return class_of_device_;
  }

  bool IsBonded() {
    return is_bonded_;
  }

  bool operator==(const Device& rhs) const {
    return this->uid_ == rhs.uid_ && this->address_ == rhs.address_ && this->device_type_ == rhs.device_type_ &&
           this->is_bonded_ == rhs.is_bonded_;
  }

 protected:
  friend class DeviceDatabase;
  friend class DualDevice;

  /**
   * @param raw_address the address of the device
   * @param device_type specify the type of device to create
   */
  Device(Address address, DeviceType device_type)
      : address_(address), device_type_(device_type), uid_(generate_uid()), name_(""), class_of_device_() {}

  /**
   * Called only by friend class DeviceDatabase
   *
   * @param address
   */
  virtual void SetAddress(Address address) {
    address_ = address;
    uid_ = generate_uid();
  }

  /**
   * Set the type of the device.
   *
   * <p>Needed by dual mode to arbitrarily set the valure to DUAL for corresponding LE/Classic devices
   *
   * @param type of device
   */
  void SetDeviceType(DeviceType type) {
    device_type_ = type;
  }

  void SetName(std::string& name) {
    name_ = name;
  }

  void SetClassOfDevice(ClassOfDevice class_of_device) {
    class_of_device_ = class_of_device;
  }

  void SetIsBonded(bool is_bonded) {
    is_bonded_ = is_bonded;
  }

 private:
  Address address_{Address::kEmpty};
  DeviceType device_type_;
  std::string uid_;
  std::string name_;
  ClassOfDevice class_of_device_;
  bool is_bonded_ = false;

  /* Uses specific information about the device to calculate a UID */
  std::string generate_uid();
};

}  // namespace bluetooth::hci
+284 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2019 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 "hci/device_database.h"

#include <memory>
#include <utility>

#include "hci/classic_device.h"
#include "hci/dual_device.h"
#include "hci/le_device.h"
#include "os/log.h"

using namespace bluetooth::hci;

std::shared_ptr<ClassicDevice> DeviceDatabase::CreateClassicDevice(Address address) {
  ClassicDevice device(address);
  const std::string uuid = device.GetUuid();
  if (AddDeviceToMap(std::move(device))) {
    return GetClassicDevice(uuid);
  }
  return std::shared_ptr<ClassicDevice>();
}

std::shared_ptr<LeDevice> DeviceDatabase::CreateLeDevice(Address address) {
  LeDevice device(address);
  const std::string uuid = device.GetUuid();
  if (AddDeviceToMap(std::move(device))) {
    return GetLeDevice(uuid);
  }
  return std::shared_ptr<LeDevice>();
}

std::shared_ptr<DualDevice> DeviceDatabase::CreateDualDevice(Address address) {
  auto classic = CreateClassicDevice(address);
  auto le = CreateLeDevice(address);
  if (classic && le) {
    DualDevice device(address, classic, le);
    std::string uuid = device.GetUuid();
    if (AddDeviceToMap(std::move(device))) {
      return GetDualDevice(uuid);
    }
  }
  LOG_WARN("Attempting to instert a DUAL device that already exists!");
  return std::shared_ptr<DualDevice>();
}

bool DeviceDatabase::RemoveDevice(const std::shared_ptr<Device>& device) {
  const DeviceType type = device->GetDeviceType();
  bool success;
  switch (type) {
    case CLASSIC:
      success = false;
      {
        std::lock_guard<std::mutex> lock(device_map_mutex_);
        auto classic_it = classic_device_map_.find(device->GetUuid());
        // If we have a record with the same key
        if (classic_it != classic_device_map_.end()) {
          classic_device_map_.erase(device->GetUuid());
          success = true;
        }
      }
      if (success) {
        ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
      } else {
        LOG_WARN("Device not in database!");
      }
      return success;
    case LE:
      success = false;
      {
        std::lock_guard<std::mutex> lock(device_map_mutex_);
        auto le_it = le_device_map_.find(device->GetUuid());
        // If we have a record with the same key
        if (le_it != le_device_map_.end()) {
          le_device_map_.erase(device->GetUuid());
          success = true;
        }
      }
      if (success) {
        ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
      } else {
        LOG_WARN("Device not in database!");
      }
      return success;
    case DUAL:
      std::shared_ptr<DualDevice> dual_device = nullptr;
      {
        std::lock_guard<std::mutex> lock(device_map_mutex_);
        auto dual_it = dual_device_map_.find(device->GetUuid());
        if (dual_it != dual_device_map_.end()) {
          dual_device = GetDualDevice(device->GetUuid());
        }
      }
      success = false;
      if (dual_device != nullptr) {
        if (RemoveDevice(dual_device->GetClassicDevice()) && RemoveDevice(dual_device->GetLeDevice())) {
          dual_device_map_.erase(device->GetUuid());
          success = true;
        }
      }
      if (success) {
        ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
      } else {
        LOG_WARN("Device not in database!");
      }
      return success;
  }
}

std::shared_ptr<ClassicDevice> DeviceDatabase::GetClassicDevice(const std::string& uuid) {
  std::lock_guard<std::mutex> lock(device_map_mutex_);
  auto it = classic_device_map_.find(uuid);
  if (it != classic_device_map_.end()) {
    return it->second;
  }
  LOG_WARN("Device '%s' not found!", uuid.c_str());
  return std::shared_ptr<ClassicDevice>();
}

std::shared_ptr<LeDevice> DeviceDatabase::GetLeDevice(const std::string& uuid) {
  std::lock_guard<std::mutex> lock(device_map_mutex_);
  auto it = le_device_map_.find(uuid);
  if (it != le_device_map_.end()) {
    return it->second;
  }
  LOG_WARN("Device '%s' not found!", uuid.c_str());
  return std::shared_ptr<LeDevice>();
}

std::shared_ptr<DualDevice> DeviceDatabase::GetDualDevice(const std::string& uuid) {
  std::lock_guard<std::mutex> lock(device_map_mutex_);
  auto it = dual_device_map_.find(uuid);
  if (it != dual_device_map_.end()) {
    return it->second;
  }
  LOG_WARN("Device '%s' not found!", uuid.c_str());
  return std::shared_ptr<DualDevice>();
}

bool DeviceDatabase::UpdateDeviceAddress(const std::shared_ptr<Device>& device, Address new_address) {
  // Hold onto device
  const DeviceType type = device->GetDeviceType();
  if (type == CLASSIC) {
    auto classic_device = GetClassicDevice(device->GetUuid());
    // This gets rid of the shared_ptr in the map
    ASSERT_LOG(RemoveDevice(device), "Failed to remove the device!");
    classic_device->SetAddress(new_address);
    // Move the value located at the pointer
    return AddDeviceToMap(std::move(*(classic_device.get())));
  } else if (type == LE) {
    auto le_device = GetLeDevice(device->GetUuid());
    // This gets rid of the shared_ptr in the map
    ASSERT_LOG(RemoveDevice(device), "Failed to remove the device!");
    le_device->SetAddress(new_address);
    // Move the value located at the pointer
    return AddDeviceToMap(std::move(*(le_device.get())));
  } else if (type == DUAL) {
    auto dual_device = GetDualDevice(device->GetUuid());
    // This gets rid of the shared_ptr in the map
    ASSERT_LOG(RemoveDevice(device), "Failed to remove the device!");
    dual_device->SetAddress(new_address);
    // Move the value located at the pointer
    return AddDeviceToMap(std::move(*(dual_device.get())));
  }
  LOG_ALWAYS_FATAL("Someone added a device type but didn't account for it here.");
  return false;
}

bool DeviceDatabase::AddDeviceToMap(ClassicDevice&& device) {
  const std::string uuid = device.GetUuid();
  bool success = false;
  {
    std::lock_guard<std::mutex> lock(device_map_mutex_);
    auto it = classic_device_map_.find(device.GetUuid());
    // If we have a record with the same key
    if (it != classic_device_map_.end()) {
      LOG_ERROR("Attempt to re-insert classic device '%s' object with same UUID", uuid.c_str());
      // We don't want to insert and overwrite
      return false;
    }
    std::shared_ptr<ClassicDevice> device_ptr = std::make_shared<ClassicDevice>(std::move(device));
    // returning the boolean value of insert success
    if (classic_device_map_
            .insert(std::pair<std::string, std::shared_ptr<ClassicDevice>>(device_ptr->GetUuid(), device_ptr))
            .second) {
      success = true;
    }
  }
  if (success) {
    ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
  } else {
    LOG_WARN("Failed to add device '%s' to map.", uuid.c_str());
  }
  return success;
}

bool DeviceDatabase::AddDeviceToMap(LeDevice&& device) {
  const std::string uuid = device.GetUuid();
  bool success = false;
  {
    std::lock_guard<std::mutex> lock(device_map_mutex_);
    auto it = le_device_map_.find(device.GetUuid());
    // If we have a record with the same key
    if (it != le_device_map_.end()) {
      LOG_ERROR("Attempt to re-insert LE device '%s' object with same UUID", uuid.c_str());
      // We don't want to insert and overwrite
      return false;
    }
    std::shared_ptr<LeDevice> device_ptr = std::make_shared<LeDevice>(std::move(device));
    // returning the boolean value of insert success
    if (le_device_map_.insert(std::pair<std::string, std::shared_ptr<LeDevice>>(device_ptr->GetUuid(), device_ptr))
            .second) {
      success = true;
    }
  }
  if (success) {
    ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
  } else {
    LOG_WARN("Failed to add device '%s' to map.", uuid.c_str());
  }
  return success;
}

bool DeviceDatabase::AddDeviceToMap(DualDevice&& device) {
  const std::string uuid = device.GetUuid();
  bool success = false;
  {
    std::lock_guard<std::mutex> lock(device_map_mutex_);
    auto it = dual_device_map_.find(device.GetUuid());
    // If we have a record with the same key
    if (it != dual_device_map_.end()) {
      LOG_ERROR("Attempt to re-insert dual device '%s' object with same UUID", uuid.c_str());
      // We don't want to insert and overwrite
      return false;
    }
    std::shared_ptr<DualDevice> device_ptr = std::make_shared<DualDevice>(std::move(device));
    // returning the boolean value of insert success
    if (dual_device_map_.insert(std::pair<std::string, std::shared_ptr<DualDevice>>(device_ptr->GetUuid(), device_ptr))
            .second) {
      success = true;
    }
  }
  if (success) {
    ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
  } else {
    LOG_WARN("Failed to add device '%s' to map.", uuid.c_str());
  }
  return success;
}

bool DeviceDatabase::WriteToDisk() {
  // TODO(optedoblivion): Implement
  // TODO(optedoblivion): FIX ME!
  // If synchronous stack dies before async write, we can miss adding device
  // post(WriteToDisk());
  // Current Solution: Synchronous disk I/O...
  std::lock_guard<std::mutex> lock(device_map_mutex_);
  // Collect information to sync to database
  // Create SQL query for insert/update
  // submit SQL
  return true;
}

bool DeviceDatabase::ReadFromDisk() {
  // TODO(optedoblivion): Implement
  // Current Solution: Synchronous disk I/O...
  std::lock_guard<std::mutex> lock(device_map_mutex_);
  return true;
}
Loading