diff options
author | Martin Brabham <optedoblivion@google.com> | 2019-08-24 01:20:36 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-08-24 01:20:36 -0700 |
commit | 332c0c2d4dc89eb1a23e9e1b3617a371b89b530a (patch) | |
tree | 145633c44ddb247ceb3eacb8f388a3ade7038bf7 | |
parent | fe9159b29fb0f97e3f58daf333e6228a88df3261 (diff) | |
parent | 58a1557b969284284b54f7d4ff683596074f3a60 (diff) | |
download | bt-332c0c2d4dc89eb1a23e9e1b3617a371b89b530a.tar.gz |
Device: Initial bluetooth device API
am: 58a1557b96
Change-Id: Ifbfecbfdc3aa6caeee3d6f19e208a26ead28d867
-rw-r--r-- | gd/hci/Android.bp | 5 | ||||
-rw-r--r-- | gd/hci/classic_device.h | 35 | ||||
-rw-r--r-- | gd/hci/device.cc | 32 | ||||
-rw-r--r-- | gd/hci/device.h | 136 | ||||
-rw-r--r-- | gd/hci/device_database.cc | 284 | ||||
-rw-r--r-- | gd/hci/device_database.h | 152 | ||||
-rw-r--r-- | gd/hci/device_database_test.cc | 133 | ||||
-rw-r--r-- | gd/hci/device_test.cc | 101 | ||||
-rw-r--r-- | gd/hci/dual_device.h | 60 | ||||
-rw-r--r-- | gd/hci/dual_device_test.cc | 81 | ||||
-rw-r--r-- | gd/hci/le_device.h | 89 |
11 files changed, 1108 insertions, 0 deletions
diff --git a/gd/hci/Android.bp b/gd/hci/Android.bp index b7ddec3cc..6d6af5070 100644 --- a/gd/hci/Android.bp +++ b/gd/hci/Android.bp @@ -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", ], } diff --git a/gd/hci/classic_device.h b/gd/hci/classic_device.h new file mode 100644 index 000000000..53ff0ba2f --- /dev/null +++ b/gd/hci/classic_device.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * 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 diff --git a/gd/hci/device.cc b/gd/hci/device.cc new file mode 100644 index 000000000..d008daec1 --- /dev/null +++ b/gd/hci/device.cc @@ -0,0 +1,32 @@ +/****************************************************************************** + * + * 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(); +} diff --git a/gd/hci/device.h b/gd/hci/device.h new file mode 100644 index 000000000..8324d00e8 --- /dev/null +++ b/gd/hci/device.h @@ -0,0 +1,136 @@ +/****************************************************************************** + * + * 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 diff --git a/gd/hci/device_database.cc b/gd/hci/device_database.cc new file mode 100644 index 000000000..6609a07b9 --- /dev/null +++ b/gd/hci/device_database.cc @@ -0,0 +1,284 @@ +/****************************************************************************** + * + * 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; +} diff --git a/gd/hci/device_database.h b/gd/hci/device_database.h new file mode 100644 index 000000000..816c096b1 --- /dev/null +++ b/gd/hci/device_database.h @@ -0,0 +1,152 @@ +/****************************************************************************** + * + * 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 <map> +#include <mutex> + +#include "hci/classic_device.h" +#include "hci/device.h" +#include "hci/dual_device.h" +#include "hci/le_device.h" +#include "os/log.h" + +namespace bluetooth::hci { + +/** + * Stores all of the paired or connected devices in the database. + * + * <p>If a device is stored here it is actively being used by the stack. + * + * <p>This database is not meant for scan results. + */ +class DeviceDatabase { + public: + DeviceDatabase() + : classic_device_map_(*new std::map<std::string, std::shared_ptr<ClassicDevice>>), + le_device_map_(*new std::map<std::string, std::shared_ptr<LeDevice>>), + dual_device_map_(*new std::map<std::string, std::shared_ptr<DualDevice>>) { + if (!ReadFromDisk()) { + LOG_WARN("First boot or missing data!"); + } + } + + /** + * Adds a device to the internal memory map and triggers a WriteToDisk. + * + * @param address private address for device + * @return weak pointer to the device or empty pointer if device already exists + */ + std::shared_ptr<ClassicDevice> CreateClassicDevice(Address address); + + /** + * Adds a device to the internal memory map and triggers a WriteToDisk. + * + * @param address private address for device + * @return weak pointer to the device or empty pointer if device already exists + */ + std::shared_ptr<LeDevice> CreateLeDevice(Address address); + + /** + * Adds a device to the internal memory map and triggers a WriteToDisk. + * + * @param address private address for device + * @return weak pointer to the device or empty pointer if device already exists + */ + std::shared_ptr<DualDevice> CreateDualDevice(Address address); + + /** + * Fetches a Classic Device matching the given uuid. + * + * @param uuid generated uuid from a Device + * @return a weak reference to the matching Device or empty shared_ptr (nullptr) + */ + std::shared_ptr<ClassicDevice> GetClassicDevice(const std::string& uuid); + + /** + * Fetches a Le Device matching the given uuid. + * + * @param uuid generated uuid from a Device + * @return a weak reference to the matching Device or empty shared_ptr (nullptr) + */ + std::shared_ptr<LeDevice> GetLeDevice(const std::string& uuid); + + /** + * Fetches a Dual Device matching the given uuid. + * + * @param uuid generated uuid from a Device + * @return a weak reference to the matching Device or empty shared_ptr (nullptr) + */ + std::shared_ptr<DualDevice> GetDualDevice(const std::string& uuid); + + /** + * Removes a device from the internal database. + * + * @param device weak pointer to device to remove from the database + * @return <code>true</code> if the device is removed + */ + bool RemoveDevice(const std::shared_ptr<Device>& device); + + /** + * Changes an address for a device. + * + * Also fixes the key mapping for the device. + * + * @param new_address this will replace the existing address + * @return <code>true</code> if updated + */ + bool UpdateDeviceAddress(const std::shared_ptr<Device>& device, Address new_address); + + // TODO(optedoblivion): Make interfaces for device modification + // We want to keep the device modification encapsulated to the DeviceDatabase. + // Pass around shared_ptr to device, device metadata only accessible via Getters. + // Choices: + // a) Have Getters/Setters on device object + // b) Have Getters/Setters on device database accepting a device object + // c) Have Getters on device object and Setters on device database accepting a device object + // I chose to go with option c for now as I think it is the best option. + + /** + * Fetches a list of classic devices. + * + * @return vector of weak pointers to classic devices + */ + std::vector<std::shared_ptr<Device>> GetClassicDevices(); + + /** + * Fetches a list of le devices + * + * @return vector of weak pointers to le devices + */ + std::vector<std::shared_ptr<Device>> GetLeDevices(); + + private: + std::mutex device_map_mutex_; + std::map<std::string, std::shared_ptr<ClassicDevice>> classic_device_map_; + std::map<std::string, std::shared_ptr<LeDevice>> le_device_map_; + std::map<std::string, std::shared_ptr<DualDevice>> dual_device_map_; + + bool AddDeviceToMap(ClassicDevice&& device); + bool AddDeviceToMap(LeDevice&& device); + bool AddDeviceToMap(DualDevice&& device); + + bool WriteToDisk(); + bool ReadFromDisk(); +}; + +} // namespace bluetooth::hci diff --git a/gd/hci/device_database_test.cc b/gd/hci/device_database_test.cc new file mode 100644 index 000000000..70dee40c2 --- /dev/null +++ b/gd/hci/device_database_test.cc @@ -0,0 +1,133 @@ +/****************************************************************************** + * + * 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 "device_database.h" +#include "classic_device.h" + +#include <gtest/gtest.h> + +using namespace bluetooth::hci; + +namespace bluetooth::hci { +namespace { + +Address address({0x01, 0x02, 0x03, 0x04, 0x05, 0x06}); +std::string address_str = "06:05:04:03:02:01"; +class DeviceDatabaseTest : public ::testing::Test { + protected: + DeviceDatabaseTest() = default; + + void SetUp() override {} + + void TearDown() override {} + + DeviceDatabase device_database_; +}; + +TEST_F(DeviceDatabaseTest, create_classic_device) { + auto classic_device = device_database_.CreateClassicDevice(address); + ASSERT_TRUE(classic_device); + ASSERT_EQ(CLASSIC, classic_device->GetDeviceType()); + ASSERT_EQ(address_str, classic_device->GetUuid()); +} + +TEST_F(DeviceDatabaseTest, create_le_device) { + auto le_device = device_database_.CreateLeDevice(address); + ASSERT_TRUE(le_device); + ASSERT_EQ(LE, le_device->GetDeviceType()); + ASSERT_EQ(address_str, le_device->GetUuid()); +} + +TEST_F(DeviceDatabaseTest, create_dual_device) { + auto dual_device = device_database_.CreateDualDevice(address); + ASSERT_TRUE(dual_device); + ASSERT_EQ(DUAL, dual_device->GetDeviceType()); + ASSERT_EQ(DUAL, dual_device->GetClassicDevice()->GetDeviceType()); + ASSERT_EQ(DUAL, dual_device->GetLeDevice()->GetDeviceType()); + ASSERT_EQ(address_str, dual_device->GetUuid()); +} + +TEST_F(DeviceDatabaseTest, create_classic_device_twice) { + auto classic_device = device_database_.CreateClassicDevice(address); + ASSERT_TRUE(classic_device); + ASSERT_EQ(CLASSIC, classic_device->GetDeviceType()); + ASSERT_EQ(address_str, classic_device->GetUuid()); + ASSERT_FALSE(device_database_.CreateClassicDevice(address)); +} + +TEST_F(DeviceDatabaseTest, create_le_device_twice) { + auto le_device = device_database_.CreateLeDevice(address); + ASSERT_TRUE(le_device); + ASSERT_EQ(LE, le_device->GetDeviceType()); + ASSERT_EQ(address_str, le_device->GetUuid()); + ASSERT_FALSE(device_database_.CreateLeDevice(address)); +} + +TEST_F(DeviceDatabaseTest, create_dual_device_twice) { + auto dual_device = device_database_.CreateDualDevice(address); + ASSERT_TRUE(dual_device); + + // Dual + ASSERT_EQ(DUAL, dual_device->GetDeviceType()); + ASSERT_EQ(address_str, dual_device->GetUuid()); + + // Classic + ASSERT_EQ(DUAL, dual_device->GetClassicDevice()->GetDeviceType()); + ASSERT_EQ(address_str, dual_device->GetClassicDevice()->GetUuid()); + + // LE + ASSERT_EQ(DUAL, dual_device->GetLeDevice()->GetDeviceType()); + ASSERT_EQ(address_str, dual_device->GetLeDevice()->GetUuid()); + + ASSERT_FALSE(device_database_.CreateDualDevice(address)); +} + +TEST_F(DeviceDatabaseTest, remove_device) { + std::shared_ptr<Device> created_device = device_database_.CreateClassicDevice(address); + ASSERT_TRUE(created_device); + ASSERT_TRUE(device_database_.RemoveDevice(created_device)); + ASSERT_TRUE(device_database_.CreateClassicDevice(address)); +} + +TEST_F(DeviceDatabaseTest, remove_device_twice) { + std::shared_ptr<Device> created_device = device_database_.CreateClassicDevice(address); + ASSERT_TRUE(device_database_.RemoveDevice(created_device)); + ASSERT_FALSE(device_database_.RemoveDevice(created_device)); +} + +TEST_F(DeviceDatabaseTest, get_nonexistent_device) { + std::shared_ptr<Device> device_ptr = device_database_.GetClassicDevice(address_str); + ASSERT_FALSE(device_ptr); +} + +TEST_F(DeviceDatabaseTest, address_modification_check) { + std::shared_ptr<Device> created_device = device_database_.CreateClassicDevice(address); + std::shared_ptr<Device> gotten_device = device_database_.GetClassicDevice(address.ToString()); + ASSERT_TRUE(created_device); + ASSERT_TRUE(gotten_device); + ASSERT_EQ(address_str, created_device->GetAddress().ToString()); + ASSERT_EQ(address_str, gotten_device->GetAddress().ToString()); + device_database_.UpdateDeviceAddress(created_device, Address({0x01, 0x01, 0x01, 0x01, 0x01, 0x01})); + ASSERT_EQ("01:01:01:01:01:01", created_device->GetAddress().ToString()); + ASSERT_EQ("01:01:01:01:01:01", gotten_device->GetAddress().ToString()); + std::shared_ptr<Device> gotten_modified_device = device_database_.GetClassicDevice("01:01:01:01:01:01"); + ASSERT_TRUE(gotten_modified_device); + ASSERT_TRUE(device_database_.RemoveDevice(gotten_modified_device)); + ASSERT_FALSE(device_database_.GetClassicDevice("01:01:01:01:01:01")); +} +} // namespace +} // namespace bluetooth::hci diff --git a/gd/hci/device_test.cc b/gd/hci/device_test.cc new file mode 100644 index 000000000..1b4fafc79 --- /dev/null +++ b/gd/hci/device_test.cc @@ -0,0 +1,101 @@ +/****************************************************************************** + * + * 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 "device.h" +#include "classic_device.h" + +#include <gtest/gtest.h> + +using namespace bluetooth::hci; + +static const char* test_addr_str = "bc:9a:78:56:34:12"; +static const uint8_t test_addr[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}; +static const Address address(test_addr); + +namespace bluetooth::hci { +namespace { +class TestableDevice : public Device { + public: + explicit TestableDevice(Address a) : Device(a, CLASSIC) {} + + void SetTheAddress() { + Address a({0x01, 0x02, 0x03, 0x04, 0x05, 0x06}); + this->SetAddress(a); + } + void SetTheClassOfDevice() { + ClassOfDevice class_of_device({0x01, 0x02, 0x03}); + this->SetClassOfDevice(class_of_device); + } + void SetTheName() { + std::string name = "Some Name"; + this->SetName(name); + } + void SetTheIsBonded() { + this->SetIsBonded(true); + } +}; +class DeviceTest : public ::testing::Test { + public: + DeviceTest() : device_(Address(test_addr)) {} + + protected: + void SetUp() override {} + + void TearDown() override {} + TestableDevice device_; +}; + +TEST_F(DeviceTest, initial_integrity) { + ASSERT_STREQ(test_addr_str, device_.GetAddress().ToString().c_str()); + ASSERT_STREQ(test_addr_str, device_.GetUuid().c_str()); + ASSERT_EQ(DeviceType::CLASSIC, device_.GetDeviceType()); + ASSERT_EQ("", device_.GetName()); +} + +TEST_F(DeviceTest, set_get_class_of_device) { + ClassOfDevice class_of_device({0x01, 0x02, 0x03}); + ASSERT_NE(class_of_device, device_.GetClassOfDevice()); + device_.SetTheClassOfDevice(); + ASSERT_EQ(class_of_device, device_.GetClassOfDevice()); +} + +TEST_F(DeviceTest, set_get_name) { + std::string name = "Some Name"; + ASSERT_EQ("", device_.GetName()); + device_.SetTheName(); + ASSERT_EQ(name, device_.GetName()); +} + +TEST_F(DeviceTest, operator_iseq) { + TestableDevice d(address); + EXPECT_EQ(device_, d); +} + +TEST_F(DeviceTest, set_address) { + ASSERT_EQ(test_addr_str, device_.GetAddress().ToString()); + device_.SetTheAddress(); + ASSERT_EQ("06:05:04:03:02:01", device_.GetAddress().ToString()); +} + +TEST_F(DeviceTest, set_bonded) { + ASSERT_FALSE(device_.IsBonded()); + device_.SetTheIsBonded(); + ASSERT_TRUE(device_.IsBonded()); +} + +} // namespace +} // namespace bluetooth::hci diff --git a/gd/hci/dual_device.h b/gd/hci/dual_device.h new file mode 100644 index 000000000..8d0801935 --- /dev/null +++ b/gd/hci/dual_device.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * + * 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/classic_device.h" +#include "hci/device.h" +#include "hci/le_device.h" + +namespace bluetooth::hci { + +/** + * A device representing a DUAL device. + * + * <p>This can be a DUAL only. + */ +class DualDevice : public Device { + public: + std::shared_ptr<Device> GetClassicDevice() { + return classic_device_; + } + + std::shared_ptr<Device> GetLeDevice() { + return le_device_; + } + + protected: + friend class DeviceDatabase; + DualDevice(Address address, std::shared_ptr<ClassicDevice> classic_device, std::shared_ptr<LeDevice> le_device) + : Device(address, DUAL), classic_device_(std::move(classic_device)), le_device_(std::move(le_device)) { + classic_device_->SetDeviceType(DUAL); + le_device_->SetDeviceType(DUAL); + } + + void SetAddress(Address address) override { + Device::SetAddress(address); + GetClassicDevice()->SetAddress(address); + GetLeDevice()->SetAddress(address); + } + + private: + std::shared_ptr<ClassicDevice> classic_device_; + std::shared_ptr<LeDevice> le_device_; +}; + +} // namespace bluetooth::hci diff --git a/gd/hci/dual_device_test.cc b/gd/hci/dual_device_test.cc new file mode 100644 index 000000000..1c4c2b01a --- /dev/null +++ b/gd/hci/dual_device_test.cc @@ -0,0 +1,81 @@ +/****************************************************************************** + * + * 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 "dual_device.h" +#include "device.h" + +#include <gtest/gtest.h> + +using namespace bluetooth::hci; + +static const char* test_addr_str = "bc:9a:78:56:34:12"; +static const uint8_t test_addr[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}; +static const Address address(test_addr); + +namespace bluetooth::hci { +namespace { +class TestableClassicDevice : public ClassicDevice { + public: + explicit TestableClassicDevice(Address a) : ClassicDevice(a) {} +}; +class TestableLeDevice : public LeDevice { + public: + explicit TestableLeDevice(Address a) : LeDevice(a) {} +}; +class TestableDevice : public DualDevice { + public: + TestableDevice(Address a, std::shared_ptr<TestableClassicDevice>& classic_device, + std::shared_ptr<TestableLeDevice>& le_device) + : DualDevice(a, classic_device, le_device) {} + + void SetTheAddress() { + Address a({0x01, 0x02, 0x03, 0x04, 0x05, 0x06}); + this->SetAddress(a); + } +}; +std::shared_ptr<TestableClassicDevice> classic_device = std::make_shared<TestableClassicDevice>(address); +std::shared_ptr<TestableLeDevice> le_device = std::make_shared<TestableLeDevice>(address); +class DualDeviceTest : public ::testing::Test { + public: + DualDeviceTest() : device_(Address(test_addr), classic_device, le_device) {} + + protected: + void SetUp() override {} + + void TearDown() override {} + TestableDevice device_; +}; + +TEST_F(DualDeviceTest, initial_integrity) { + Address a = device_.GetAddress(); + ASSERT_EQ(test_addr_str, a.ToString()); + + ASSERT_EQ(DUAL, device_.GetClassicDevice()->GetDeviceType()); + ASSERT_EQ(a.ToString(), device_.GetClassicDevice()->GetAddress().ToString()); + + ASSERT_EQ(DUAL, device_.GetLeDevice()->GetDeviceType()); + ASSERT_EQ(a.ToString(), device_.GetLeDevice()->GetAddress().ToString()); + + device_.SetTheAddress(); + + ASSERT_EQ("06:05:04:03:02:01", device_.GetAddress().ToString()); + ASSERT_EQ("06:05:04:03:02:01", device_.GetClassicDevice()->GetAddress().ToString()); + ASSERT_EQ("06:05:04:03:02:01", device_.GetLeDevice()->GetAddress().ToString()); +} + +} // namespace +} // namespace bluetooth::hci diff --git a/gd/hci/le_device.h b/gd/hci/le_device.h new file mode 100644 index 000000000..9ffd8ab20 --- /dev/null +++ b/gd/hci/le_device.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * + * 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 { + +/** + * TODO(optedoblivion): Build out AddressType getter/setter + */ +enum AddressType {}; + +/** + * A device representing a LE device. + * + * <p>This can be a LE only or a piece of a DUAL MODE device. + * + * <p>LE specific public address logic goes here. + */ +class LeDevice : public Device { + public: + void SetPublicAddress(Address public_address) { + public_address_ = public_address; + } + + Address GetPublicAddress() { + return public_address_; + } + + void SetIrk(uint8_t irk) { + irk_ = irk; + // TODO(optedoblivion): Set derived Address + } + + uint8_t GetIrk() { + return irk_; + } + + protected: + friend class DeviceDatabase; + // TODO(optedoblivion): How to set public address. Do I set it when no IRK is known? + // Right now my thought is to do this: + // 1. Construct LeDevice with address of all 0s + // 2. IF NO IRK AND NO PRIVATE ADDRESS: (i.e. nothing in disk cache) + // a. Hopefully pairing will happen + // b. Pending successful pairing get the IRK and Private Address + // c. Set Both to device. + // (d). If available set IRK to the controller (later iteration) + // [#3 should indicate we have a bug] + // 3. IF YES IRK AND NO PRIVATE ADDRESS: (Partial Disk Cache Information) + // a. Set IRK + // b. Generate Private Address + // c. Set Private Address to device + // (d). If available set IRK to the controller (later iteration) + // 4. IF YES IRK AND YES PRIVATE ADDRESS: (i.e. Disk cache hit) + // a. Construct with private address + // b. Set IRK + // (c). If available set IRK to the controller (later iteration) + // 5. IF NO IRK AND YES PRIVATE ADDRESS (we have a bug) + // a1. -Construct with private address- + // b. -Indicate we need to repair or query for IRK?- + // + // or + // + // a2. Don't use class + explicit LeDevice(Address address) : Device(address, DeviceType::LE), public_address_(), irk_(0) {} + + private: + Address public_address_; + uint8_t irk_; +}; + +} // namespace bluetooth::hci |