Merge "ClassicSecurity: Remove extra Event conversion"
diff --git a/audio_bluetooth_hw/audio_bluetooth_hw.cc b/audio_bluetooth_hw/audio_bluetooth_hw.cc
index 5d0b43a..887c4e3 100644
--- a/audio_bluetooth_hw/audio_bluetooth_hw.cc
+++ b/audio_bluetooth_hw/audio_bluetooth_hw.cc
@@ -102,7 +102,8 @@
static int adev_dump(const audio_hw_device_t* device, int fd) { return 0; }
static int adev_close(hw_device_t* device) {
- free(device);
+ auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(device);
+ delete bluetooth_device;
return 0;
}
@@ -111,7 +112,7 @@
LOG(VERBOSE) << __func__ << ": name=[" << name << "]";
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
- auto bluetooth_audio_device = new BluetoothAudioDevice;
+ auto bluetooth_audio_device = new BluetoothAudioDevice{};
struct audio_hw_device* adev = &bluetooth_audio_device->audio_device_;
if (!adev) return -ENOMEM;
diff --git a/audio_bluetooth_hw/stream_apis.cc b/audio_bluetooth_hw/stream_apis.cc
index 7b01c32..d809b57 100644
--- a/audio_bluetooth_hw/stream_apis.cc
+++ b/audio_bluetooth_hw/stream_apis.cc
@@ -635,7 +635,7 @@
struct audio_stream_out** stream_out,
const char* address __unused) {
*stream_out = nullptr;
- auto* out = new BluetoothStreamOut;
+ auto* out = new BluetoothStreamOut{};
if (!out->bluetooth_output_.SetUp(devices)) {
delete out;
return -EINVAL;
diff --git a/audio_bluetooth_hw/stream_apis.h b/audio_bluetooth_hw/stream_apis.h
index e319d95..c894d1e 100644
--- a/audio_bluetooth_hw/stream_apis.h
+++ b/audio_bluetooth_hw/stream_apis.h
@@ -46,7 +46,7 @@
struct BluetoothStreamOut {
// Must be the first member so it can be cast from audio_stream
// or audio_stream_out pointer
- audio_stream_out stream_out_;
+ audio_stream_out stream_out_{};
::android::bluetooth::audio::BluetoothAudioPortOut bluetooth_output_;
int64_t last_write_time_us_;
// Audio PCM Configs
@@ -66,7 +66,7 @@
struct BluetoothAudioDevice {
// Important: device must be first as an audio_hw_device* may be cast to
// BluetoothAudioDevice* when the type is implicitly known.
- audio_hw_device audio_device_;
+ audio_hw_device audio_device_{};
// protect against device->output and stream_out from being inconsistent
std::mutex mutex_;
std::list<BluetoothStreamOut*> opened_stream_outs_ =
diff --git a/binder/android/bluetooth/IBluetoothA2dp.aidl b/binder/android/bluetooth/IBluetoothA2dp.aidl
index 3b406cc..39157e5 100644
--- a/binder/android/bluetooth/IBluetoothA2dp.aidl
+++ b/binder/android/bluetooth/IBluetoothA2dp.aidl
@@ -52,4 +52,5 @@
int supportsOptionalCodecs(in BluetoothDevice device);
int getOptionalCodecsEnabled(in BluetoothDevice device);
oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value);
+ int getPriority(in BluetoothDevice device);
}
diff --git a/binder/android/bluetooth/IBluetoothHeadset.aidl b/binder/android/bluetooth/IBluetoothHeadset.aidl
index bc8fa05..0edac14 100644
--- a/binder/android/bluetooth/IBluetoothHeadset.aidl
+++ b/binder/android/bluetooth/IBluetoothHeadset.aidl
@@ -63,4 +63,6 @@
boolean setActiveDevice(in BluetoothDevice device);
BluetoothDevice getActiveDevice();
boolean isInbandRingingEnabled();
+ boolean setPriority(in BluetoothDevice device, int connectionPolicy);
+ int getPriority(in BluetoothDevice device);
}
diff --git a/btif/co/bta_av_co.cc b/btif/co/bta_av_co.cc
index 60b407c..b2a02d9 100644
--- a/btif/co/bta_av_co.cc
+++ b/btif/co/bta_av_co.cc
@@ -1378,7 +1378,7 @@
APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x delay:0x%x", __func__,
peer_address.ToString().c_str(), bta_av_handle, delay);
- btif_av_set_audio_delay(delay);
+ btif_av_set_audio_delay(peer_address, delay);
}
void BtaAvCo::UpdateMtu(tBTA_AV_HNDL bta_av_handle,
diff --git a/btif/include/btif_av.h b/btif/include/btif_av.h
index 1c596f9..adbc333 100644
--- a/btif/include/btif_av.h
+++ b/btif/include/btif_av.h
@@ -163,9 +163,16 @@
/**
* Set the audio delay for the stream.
*
+ * @param peer_address the address of the peer to report
* @param delay the delay to set in units of 1/10ms
*/
-void btif_av_set_audio_delay(uint16_t delay);
+void btif_av_set_audio_delay(const RawAddress& peer_address, uint16_t delay);
+
+/**
+ * Get the audio delay for the stream.
+ * @param none
+ */
+uint16_t btif_av_get_audio_delay(void);
/**
* Reset the audio delay and count of audio bytes sent to zero.
diff --git a/btif/src/bluetooth.cc b/btif/src/bluetooth.cc
index 8dfe73a..56f0b9b 100644
--- a/btif/src/bluetooth.cc
+++ b/btif/src/bluetooth.cc
@@ -63,6 +63,7 @@
#include "btsnoop.h"
#include "btsnoop_mem.h"
#include "common/address_obfuscator.h"
+#include "common/metric_id_allocator.h"
#include "common/metrics.h"
#include "device/include/interop.h"
#include "main/shim/dumpsys.h"
@@ -460,6 +461,11 @@
address);
}
+static int get_metric_id(const RawAddress& address) {
+ return bluetooth::common::MetricIdAllocator::GetInstance().AllocateId(
+ address);
+}
+
EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
sizeof(bluetoothInterface),
init,
@@ -496,4 +502,5 @@
interop_database_add,
get_avrcp_service,
obfuscate_address,
+ get_metric_id,
};
diff --git a/btif/src/btif_a2dp_source.cc b/btif/src/btif_a2dp_source.cc
index 4ad9368..45b2e3f 100644
--- a/btif/src/btif_a2dp_source.cc
+++ b/btif/src/btif_a2dp_source.cc
@@ -397,6 +397,7 @@
}
if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
bluetooth::audio::a2dp::start_session();
+ bluetooth::audio::a2dp::set_remote_delay(btif_av_get_audio_delay());
BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
bluetooth::common::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
} else if (btif_av_is_a2dp_offload_enabled()) {
diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc
index 55350e0..4f6f0ca 100644
--- a/btif/src/btif_av.cc
+++ b/btif/src/btif_av.cc
@@ -282,6 +282,10 @@
void SetSilence(bool silence) { is_silenced_ = silence; };
+ // AVDTP delay reporting in 1/10 milliseconds
+ void SetDelayReport(uint16_t delay) { delay_report_ = delay; };
+ uint16_t GetDelayReport() const { return delay_report_; };
+
/**
* Check whether any of the flags specified by the bitlags mask is set.
*
@@ -330,6 +334,7 @@
uint8_t flags_;
bool self_initiated_connection_;
bool is_silenced_;
+ uint16_t delay_report_;
};
class BtifAvSource {
@@ -864,7 +869,8 @@
av_open_on_rc_timer_(nullptr),
edr_(0),
flags_(0),
- self_initiated_connection_(false) {}
+ self_initiated_connection_(false),
+ delay_report_(0) {}
BtifAvPeer::~BtifAvPeer() { alarm_free(av_open_on_rc_timer_); }
@@ -3260,6 +3266,7 @@
dprintf(fd, " Support 3Mbps: %s\n", peer.Is3Mbps() ? "true" : "false");
dprintf(fd, " Self Initiated Connection: %s\n",
peer.SelfInitiatedConnection() ? "true" : "false");
+ dprintf(fd, " Delay Reporting: %u\n", peer.GetDelayReport());
}
static void btif_debug_av_source_dump(int fd) {
@@ -3294,9 +3301,23 @@
btif_debug_av_sink_dump(fd);
}
-void btif_av_set_audio_delay(uint16_t delay) {
+void btif_av_set_audio_delay(const RawAddress& peer_address, uint16_t delay) {
btif_a2dp_control_set_audio_delay(delay);
- bluetooth::audio::a2dp::set_remote_delay(delay);
+ BtifAvPeer* peer = btif_av_find_peer(peer_address);
+ if (peer != nullptr && peer->IsSink()) {
+ peer->SetDelayReport(delay);
+ if (peer->IsActivePeer()) {
+ bluetooth::audio::a2dp::set_remote_delay(peer->GetDelayReport());
+ }
+ }
+}
+
+uint16_t btif_av_get_audio_delay() {
+ BtifAvPeer* peer = btif_av_find_active_peer();
+ if (peer != nullptr && peer->IsSink()) {
+ return peer->GetDelayReport();
+ }
+ return 0;
}
void btif_av_reset_audio_delay(void) { btif_a2dp_control_reset_audio_delay(); }
diff --git a/btif/src/btif_config.cc b/btif/src/btif_config.cc
index 924de43e..4b5c854 100644
--- a/btif/src/btif_config.cc
+++ b/btif/src/btif_config.cc
@@ -29,9 +29,11 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <functional>
#include <mutex>
#include <sstream>
#include <string>
+#include <unordered_map>
#include "bt_types.h"
#include "btcore/include/module.h"
@@ -41,6 +43,7 @@
//#include "btif_keystore.h"
#include "btif_util.h"
#include "common/address_obfuscator.h"
+#include "common/metric_id_allocator.h"
#include "main/shim/config.h"
#include "main/shim/shim.h"
#include "osi/include/alarm.h"
@@ -50,6 +53,7 @@
#include "osi/include/log.h"
#include "osi/include/osi.h"
#include "osi/include/properties.h"
+#include "raw_address.h"
#define BT_CONFIG_SOURCE_TAG_NUM 1010001
@@ -68,8 +72,11 @@
#define BT_CONFIG_METRICS_SECTION "Metrics"
#define BT_CONFIG_METRICS_SALT_256BIT "Salt256Bit"
+#define BT_CONFIG_METRICS_ID_KEY "MetricsId"
+
// using bluetooth::BtifKeystore;
using bluetooth::common::AddressObfuscator;
+using bluetooth::common::MetricIdAllocator;
// TODO(armansito): Find a better way than searching by a hardcoded path.
#if defined(OS_GENERIC)
@@ -191,6 +198,63 @@
AddressObfuscator::GetInstance()->Initialize(metrics_salt);
}
+/**
+ * Initialize metric id allocator by reading metric_id from config by mac
+ * address. If there is no metric id for a mac address, then allocate it a new
+ * metric id.
+ */
+static void init_metric_id_allocator() {
+ std::unordered_map<RawAddress, int> paired_device_map;
+
+ // When user update the system, there will be devices paired with older
+ // version of android without a metric id.
+ std::vector<RawAddress> addresses_without_id;
+
+ for (auto& section : btif_config_sections()) {
+ auto& section_name = section.name;
+ RawAddress mac_address;
+ if (!RawAddress::FromString(section_name, mac_address)) {
+ continue;
+ }
+ // if the section name is a mac address
+ bool is_valid_id_found = false;
+ if (btif_config_exist(section_name, BT_CONFIG_METRICS_ID_KEY)) {
+ // there is one metric id under this mac_address
+ int id = 0;
+ btif_config_get_int(section_name, BT_CONFIG_METRICS_ID_KEY, &id);
+ if (MetricIdAllocator::IsValidId(id)) {
+ paired_device_map[mac_address] = id;
+ is_valid_id_found = true;
+ }
+ }
+ if (!is_valid_id_found) {
+ addresses_without_id.push_back(mac_address);
+ }
+ }
+
+ // Initialize MetricIdAllocator
+ MetricIdAllocator::Callback save_device_callback =
+ [](const RawAddress& address, const int id) {
+ return btif_config_set_int(address.ToString(), BT_CONFIG_METRICS_ID_KEY,
+ id);
+ };
+ MetricIdAllocator::Callback forget_device_callback =
+ [](const RawAddress& address, const int id) {
+ return btif_config_remove(address.ToString(), BT_CONFIG_METRICS_ID_KEY);
+ };
+ if (!MetricIdAllocator::GetInstance().Init(
+ paired_device_map, std::move(save_device_callback),
+ std::move(forget_device_callback))) {
+ LOG(FATAL) << __func__ << "Failed to initialize MetricIdAllocator";
+ }
+
+ // Add device_without_id
+ for (auto& address : addresses_without_id) {
+ MetricIdAllocator::GetInstance().AllocateId(address);
+ MetricIdAllocator::GetInstance().SaveDevice(address);
+ }
+}
+
static std::recursive_mutex config_lock; // protects operations on |config|.
static std::unique_ptr<config_t> config;
static alarm_t* config_timer;
@@ -262,6 +326,9 @@
// Read or set metrics 256 bit hashing salt
read_or_set_metrics_salt();
+ // Initialize MetricIdAllocator
+ init_metric_id_allocator();
+
// TODO(sharvil): use a non-wake alarm for this once we have
// API support for it. There's no need to wake the system to
// write back to disk.
@@ -326,6 +393,7 @@
config_timer = NULL;
std::unique_lock<std::recursive_mutex> lock(config_lock);
+ MetricIdAllocator::GetInstance().Close();
config.reset();
return future_new_immediate(FUTURE_SUCCESS);
}
diff --git a/btif/src/btif_dm.cc b/btif/src/btif_dm.cc
index 71489f7..8b8d829 100644
--- a/btif/src/btif_dm.cc
+++ b/btif/src/btif_dm.cc
@@ -60,6 +60,7 @@
#include "btif_storage.h"
#include "btif_util.h"
#include "btu.h"
+#include "common/metric_id_allocator.h"
#include "common/metrics.h"
#include "device/include/controller.h"
#include "device/include/interop.h"
@@ -74,6 +75,7 @@
#include "stack_config.h"
using bluetooth::Uuid;
+using bluetooth::common::MetricIdAllocator;
/******************************************************************************
* Constants & Macros
*****************************************************************************/
@@ -293,6 +295,15 @@
static void btif_dm_send_bond_state_changed(RawAddress address, bt_bond_state_t bond_state) {
do_in_jni_thread(FROM_HERE, base::BindOnce([](RawAddress address, bt_bond_state_t bond_state) {
btif_stats_add_bond_event(address, BTIF_DM_FUNC_BOND_STATE_CHANGED, bond_state);
+ if (bond_state == BT_BOND_STATE_NONE) {
+ MetricIdAllocator::GetInstance().ForgetDevice(address);
+ } else if (bond_state == BT_BOND_STATE_BONDED) {
+ MetricIdAllocator::GetInstance().AllocateId(address);
+ if (!MetricIdAllocator::GetInstance().SaveDevice(address)) {
+ LOG(FATAL) << __func__ << ": Fail to save metric id for device "
+ << address;
+ }
+ }
HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, BT_STATUS_SUCCESS, &address, bond_state);
}, address, bond_state));
}
@@ -545,6 +556,15 @@
BTIF_TRACE_DEBUG("%s: state=%d, prev_state=%d, sdp_attempts = %d", __func__,
state, pairing_cb.state, pairing_cb.sdp_attempts);
+ if (state == BT_BOND_STATE_NONE) {
+ MetricIdAllocator::GetInstance().ForgetDevice(bd_addr);
+ } else if (state == BT_BOND_STATE_BONDED) {
+ MetricIdAllocator::GetInstance().AllocateId(bd_addr);
+ if (!MetricIdAllocator::GetInstance().SaveDevice(bd_addr)) {
+ LOG(FATAL) << __func__ << ": Fail to save metric id for device "
+ << bd_addr;
+ }
+ }
auto tmp = bd_addr;
HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, status, &tmp, state);
@@ -1191,6 +1211,7 @@
// Do not call bond_state_changed_cb yet. Wait until remote service
// discovery is complete
} else {
+ bool is_bonded_device_removed = false;
// Map the HCI fail reason to bt status
switch (p_auth_cmpl->fail_reason) {
case HCI_ERR_PAGE_TIMEOUT:
@@ -1209,14 +1230,16 @@
break;
case HCI_ERR_PAIRING_NOT_ALLOWED:
- btif_storage_remove_bonded_device(&bd_addr);
+ is_bonded_device_removed =
+ (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
status = BT_STATUS_AUTH_REJECTED;
break;
/* map the auth failure codes, so we can retry pairing if necessary */
case HCI_ERR_AUTH_FAILURE:
case HCI_ERR_KEY_MISSING:
- btif_storage_remove_bonded_device(&bd_addr);
+ is_bonded_device_removed =
+ (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
[[fallthrough]];
case HCI_ERR_HOST_REJECT_SECURITY:
case HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE:
@@ -1247,9 +1270,14 @@
/* Remove Device as bonded in nvram as authentication failed */
BTIF_TRACE_DEBUG("%s(): removing hid pointing device from nvram",
__func__);
- btif_storage_remove_bonded_device(&bd_addr);
+ is_bonded_device_removed =
+ (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
}
- bond_state_changed(status, bd_addr, state);
+ // Report bond state change to java only if we are bonding to a device or
+ // a device is removed from the pairing list.
+ if (pairing_cb.state == BT_BOND_STATE_BONDING || is_bonded_device_removed) {
+ bond_state_changed(status, bd_addr, state);
+ }
}
}
@@ -2414,6 +2442,11 @@
BTIF_TRACE_EVENT("%s: accept=%d", __func__, accept);
if (bluetooth::shim::is_gd_shim_enabled()) {
+ if (pin_code == nullptr) {
+ LOG_ERROR("Pin code must be not null with GD shim enabled");
+ return BT_STATUS_FAIL;
+ }
+
uint8_t tmp_dev_type = 0;
uint8_t tmp_addr_type = 0;
BTM_ReadDevInfo(*bd_addr, &tmp_dev_type, &tmp_addr_type);
diff --git a/btif/src/btif_hf.cc b/btif/src/btif_hf.cc
index 04ad011..99063fd 100644
--- a/btif/src/btif_hf.cc
+++ b/btif/src/btif_hf.cc
@@ -319,12 +319,48 @@
case BTA_AG_OPEN_EVT:
// Check if an outoging connection is pending
if (btif_hf_cb[idx].is_initiator) {
+ if ((p_data->open.status != BTA_AG_SUCCESS) &&
+ btif_hf_cb[idx].state != BTHF_CONNECTION_STATE_CONNECTING) {
+ if (p_data->open.bd_addr == btif_hf_cb[idx].connected_bda) {
+ LOG(WARNING) << __func__ << ": btif_hf_cb state["
+ << p_data->open.status
+ << "] is not expected, possible connection collision, "
+ "ignoring AG open "
+ "failure event for the same device "
+ << p_data->open.bd_addr;
+ } else {
+ LOG(WARNING) << __func__ << ": btif_hf_cb state["
+ << p_data->open.status
+ << "] is not expected, possible connection collision, "
+ "ignoring AG open failure "
+ "event for the different devices btif_hf_cb bda: "
+ << btif_hf_cb[idx].connected_bda
+ << ", p_data bda: " << p_data->open.bd_addr
+ << ", report disconnect state for p_data bda.";
+ bt_hf_callbacks->ConnectionStateCallback(
+ BTHF_CONNECTION_STATE_DISCONNECTED, &(p_data->open.bd_addr));
+ }
+ break;
+ }
+
CHECK_EQ(btif_hf_cb[idx].state, BTHF_CONNECTION_STATE_CONNECTING)
<< "Control block must be in connecting state when initiating";
CHECK(!btif_hf_cb[idx].connected_bda.IsEmpty())
<< "Remote device address must not be empty when initiating";
- CHECK_EQ(btif_hf_cb[idx].connected_bda, p_data->open.bd_addr)
- << "Incoming message's address must match expected one";
+ if (btif_hf_cb[idx].connected_bda != p_data->open.bd_addr) {
+ LOG(WARNING) << __func__
+ << ": possible connection collision, ignore the "
+ "outgoing connection for the "
+ "different devices btif_hf_cb bda: "
+ << btif_hf_cb[idx].connected_bda
+ << ", p_data bda: " << p_data->open.bd_addr
+ << ", report disconnect state for btif_hf_cb bda.";
+ bt_hf_callbacks->ConnectionStateCallback(
+ BTHF_CONNECTION_STATE_DISCONNECTED,
+ &(btif_hf_cb[idx].connected_bda));
+ reset_control_block(&btif_hf_cb[idx]);
+ btif_queue_advance();
+ }
}
if (p_data->open.status == BTA_AG_SUCCESS) {
// In case this is an incoming connection
diff --git a/common/metric_id_allocator.cc b/common/metric_id_allocator.cc
index 2667fa6..005ef3a 100644
--- a/common/metric_id_allocator.cc
+++ b/common/metric_id_allocator.cc
@@ -27,9 +27,9 @@
namespace common {
-const std::string MetricIdAllocator::LOG_TAG = "BluetoothMetricIdAllocator";
+const std::string MetricIdAllocator::LOGGING_TAG = "BluetoothMetricIdAllocator";
const size_t MetricIdAllocator::kMaxNumUnpairedDevicesInMemory = 200;
-const size_t MetricIdAllocator::kMaxNumPairedDevicesInMemory = 400;
+const size_t MetricIdAllocator::kMaxNumPairedDevicesInMemory = 65000;
const int MetricIdAllocator::kMinId = 1;
const int MetricIdAllocator::kMaxId = 65534; // 2^16 - 2
@@ -42,14 +42,13 @@
"kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory");
MetricIdAllocator::MetricIdAllocator()
- : paired_device_cache_(kMaxNumPairedDevicesInMemory, LOG_TAG,
- [this](RawAddress dummy, int to_remove) {
- this->id_set_.erase(to_remove);
+ : paired_device_cache_(kMaxNumPairedDevicesInMemory, LOGGING_TAG,
+ [this](RawAddress mac_address, int id) {
+ ForgetDevicePostprocess(mac_address, id);
}),
- temporary_device_cache_(kMaxNumUnpairedDevicesInMemory, LOG_TAG,
- [this](RawAddress dummy, int to_remove) {
- this->id_set_.erase(to_remove);
- }) {}
+ temporary_device_cache_(
+ kMaxNumUnpairedDevicesInMemory, LOGGING_TAG,
+ [this](RawAddress dummy, int id) { this->id_set_.erase(id); }) {}
bool MetricIdAllocator::Init(
const std::unordered_map<RawAddress, int>& paired_device_map,
@@ -62,7 +61,7 @@
// init paired_devices_map
if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
LOG(FATAL)
- << LOG_TAG
+ << LOGGING_TAG
<< "Paired device map is bigger than kMaxNumPairedDevicesInMemory";
// fail loudly to let caller know
return false;
@@ -71,7 +70,7 @@
next_id_ = kMinId;
for (const std::pair<RawAddress, int>& p : paired_device_map) {
if (p.second < kMinId || p.second > kMaxId) {
- LOG(FATAL) << LOG_TAG << "Invalid Bluetooth Metric Id in config";
+ LOG(FATAL) << LOGGING_TAG << "Invalid Bluetooth Metric Id in config";
}
paired_device_cache_.Put(p.first, p.second);
id_set_.insert(p.second);
@@ -130,7 +129,7 @@
next_id_++;
if (next_id_ > kMaxId) {
next_id_ = kMinId;
- LOG(WARNING) << LOG_TAG << "Bluetooth metric id overflow.";
+ LOG(WARNING) << LOGGING_TAG << "Bluetooth metric id overflow.";
}
}
id = next_id_++;
@@ -147,26 +146,55 @@
bool MetricIdAllocator::SaveDevice(const RawAddress& mac_address) {
std::lock_guard<std::mutex> lock(id_allocator_mutex_);
int id = 0;
- bool success = temporary_device_cache_.Get(mac_address, &id);
- success &= temporary_device_cache_.Remove(mac_address);
- if (success) {
- paired_device_cache_.Put(mac_address, id);
- success = save_id_callback_(mac_address, id);
+ if (paired_device_cache_.Get(mac_address, &id)) {
+ return true;
}
- return success;
+ if (!temporary_device_cache_.Get(mac_address, &id)) {
+ LOG(ERROR) << LOGGING_TAG
+ << "Failed to save device because device is not in "
+ << "temporary_device_cache_";
+ return false;
+ }
+ if (!temporary_device_cache_.Remove(mac_address)) {
+ LOG(ERROR) << LOGGING_TAG
+ << "Failed to remove device from temporary_device_cache_";
+ return false;
+ }
+ paired_device_cache_.Put(mac_address, id);
+ if (!save_id_callback_(mac_address, id)) {
+ LOG(ERROR) << LOGGING_TAG
+ << "Callback returned false after saving the device";
+ return false;
+ }
+ return true;
}
// call this function when a device is forgotten
-bool MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
+void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
std::lock_guard<std::mutex> lock(id_allocator_mutex_);
int id = 0;
- bool success = paired_device_cache_.Get(mac_address, &id);
- success &= paired_device_cache_.Remove(mac_address);
- if (success) {
- id_set_.erase(id);
- success = forget_device_callback_(mac_address, id);
+ if (!paired_device_cache_.Get(mac_address, &id)) {
+ LOG(ERROR) << LOGGING_TAG
+ << "Failed to forget device because device is not in "
+ << "paired_device_cache_";
+ return;
}
- return success;
+ if (!paired_device_cache_.Remove(mac_address)) {
+ LOG(ERROR) << LOGGING_TAG
+ << "Failed to remove device from paired_device_cache_";
+ return;
+ }
+ ForgetDevicePostprocess(mac_address, id);
+}
+
+bool MetricIdAllocator::IsValidId(const int id) {
+ return id >= kMinId && id <= kMaxId;
+}
+
+void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& mac_address,
+ const int id) {
+ id_set_.erase(id);
+ forget_device_callback_(mac_address, id);
}
} // namespace common
diff --git a/common/metric_id_allocator.h b/common/metric_id_allocator.h
index f941fd0..63236e4 100644
--- a/common/metric_id_allocator.h
+++ b/common/metric_id_allocator.h
@@ -98,16 +98,25 @@
* Delete the id for a device to be forgotten
*
* @param mac_address mac address of Bluetooth device
+ */
+ void ForgetDevice(const RawAddress& mac_address);
+
+ /**
+ * Check if an id is valid.
+ * The id should be less than or equal to kMaxId and bigger than or equal to
+ * kMinId
+ *
+ * @param mac_address mac address of Bluetooth device
* @return true if delete successfully
*/
- bool ForgetDevice(const RawAddress& mac_address);
+ static bool IsValidId(const int id);
protected:
// Singleton
MetricIdAllocator();
private:
- static const std::string LOG_TAG;
+ static const std::string LOGGING_TAG;
mutable std::mutex id_allocator_mutex_;
LruCache<RawAddress, int> paired_device_cache_;
@@ -119,6 +128,8 @@
Callback save_id_callback_;
Callback forget_device_callback_;
+ void ForgetDevicePostprocess(const RawAddress& mac_address, const int id);
+
// delete copy constructor for singleton
MetricIdAllocator(MetricIdAllocator const&) = delete;
MetricIdAllocator& operator=(MetricIdAllocator const&) = delete;
diff --git a/common/metric_id_allocator_unittest.cc b/common/metric_id_allocator_unittest.cc
index 416b671..ccc1576 100644
--- a/common/metric_id_allocator_unittest.cc
+++ b/common/metric_id_allocator_unittest.cc
@@ -165,14 +165,14 @@
EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 3})));
EXPECT_EQ(dummy, 176);
- // should fail, since id had been saved
- EXPECT_FALSE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 0})));
+ // should be true but callback won't be called, since id had been saved
+ EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 0})));
EXPECT_EQ(dummy, 176);
// forget
- EXPECT_FALSE(allocator.ForgetDevice(RawAddress({0, 0, 0, 0, 0, 1})));
+ allocator.ForgetDevice(RawAddress({0, 0, 0, 0, 0, 1}));
EXPECT_EQ(dummy, 176);
- EXPECT_TRUE(allocator.ForgetDevice(RawAddress({0, 0, 0, 0, 0, 2})));
+ allocator.ForgetDevice(RawAddress({0, 0, 0, 0, 0, 2}));
EXPECT_EQ(dummy, 88);
EXPECT_TRUE(allocator.Close());
@@ -183,7 +183,7 @@
// preset a full map
std::unordered_map<RawAddress, int> paired_device_map =
generateAddresses(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
- int dummy = 22;
+ int dummy = 243;
int* pointer = &dummy;
MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
const int) {
@@ -192,7 +192,7 @@
};
MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
const int) {
- *pointer = *pointer / 2;
+ *pointer = *pointer / 3;
return true;
};
@@ -222,7 +222,8 @@
// save it and make sure the callback is called
EXPECT_TRUE(allocator.SaveDevice(kthAddress(key)));
- EXPECT_EQ(dummy, 44);
+ EXPECT_EQ(dummy, 162); // one key is evicted, another key is saved so *2/3
+
// paired: 1, 2 ... 199, 200,
// scanned:
@@ -233,13 +234,13 @@
// key == 200
// should fail, since id of device is not allocated
EXPECT_FALSE(allocator.SaveDevice(kthAddress(key + 1)));
- EXPECT_EQ(dummy, 44);
+ EXPECT_EQ(dummy, 162);
// paired: 1, 2 ... 199, 200,
// scanned: 0
EXPECT_EQ(allocator.AllocateId(kthAddress(key + 1)), id++);
EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 1)));
- EXPECT_EQ(dummy, 88);
+ EXPECT_EQ(dummy, 108); // one key is evicted, another key is saved so *2/3,
// paired: 2 ... 199, 200, 201
// scanned: 0
@@ -253,23 +254,24 @@
// paired: 2 ... 199, 200, 201,
// scanned: 0, 1, 202, 203
- dummy = 44;
+ dummy = 9;
EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
- EXPECT_EQ(dummy, 88);
+ EXPECT_EQ(dummy, 6); // one key is evicted, another key is saved so *2/3,
EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 3)));
- EXPECT_EQ(dummy, 176);
+ EXPECT_EQ(dummy, 4); // one key is evicted, another key is saved so *2/3,
// paired: 4 ... 199, 200, 201, 202, 203
// scanned: 0, 1
- // should fail, since id had been saved
- EXPECT_FALSE(allocator.SaveDevice(kthAddress(key + 2)));
- EXPECT_EQ(dummy, 176);
+ // should be true but callback won't be called, since id had been saved
+ EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
+ EXPECT_EQ(dummy, 4);
+ dummy = 27;
// forget
- EXPECT_FALSE(allocator.ForgetDevice(kthAddress(key + 200)));
- EXPECT_EQ(dummy, 176);
- EXPECT_TRUE(allocator.ForgetDevice(kthAddress(key + 2)));
- EXPECT_EQ(dummy, 88);
+ allocator.ForgetDevice(kthAddress(key + 200));
+ EXPECT_EQ(dummy, 27); // should fail, no such a key
+ allocator.ForgetDevice(kthAddress(key + 2));
+ EXPECT_EQ(dummy, 9);
// paired: 4 ... 199, 200, 201, 203
// scanned: 0, 1
@@ -281,25 +283,27 @@
// scanned: 0, 1, 202, 204, 205
EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
- EXPECT_EQ(dummy, 176);
- EXPECT_FALSE(allocator.SaveDevice(kthAddress(key + 3)));
- EXPECT_EQ(dummy, 176);
+ EXPECT_EQ(dummy, 18); // no key is evicted, a key is saved so *2,
+
+ // should be true but callback won't be called, since id had been saved
+ EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 3)));
+ EXPECT_EQ(dummy, 18); // no such a key in scanned
EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 4)));
- EXPECT_EQ(dummy, 352);
- // paired: 6 ... 199, 200, 201, 203, 202, 204
+ EXPECT_EQ(dummy, 12); // one key is evicted, another key is saved so *2/3,
+ // paired: 5 6 ... 199, 200, 201, 203, 202, 204
// scanned: 0, 1, 205
// verify paired:
- for (key = 6; key <= 199; key++) {
- dummy = 10;
- EXPECT_TRUE(allocator.ForgetDevice(kthAddress(key)));
- EXPECT_EQ(dummy, 5);
+ for (key = 5; key <= 199; key++) {
+ dummy = 3;
+ allocator.ForgetDevice(kthAddress(key));
+ EXPECT_EQ(dummy, 1);
}
for (size_t k = MetricIdAllocator::kMaxNumPairedDevicesInMemory;
k <= MetricIdAllocator::kMaxNumPairedDevicesInMemory + 4; k++) {
- dummy = 10;
- EXPECT_TRUE(allocator.ForgetDevice(kthAddress(k)));
- EXPECT_EQ(dummy, 5);
+ dummy = 3;
+ allocator.ForgetDevice(kthAddress(k));
+ EXPECT_EQ(dummy, 1);
}
// verify scanned
@@ -397,14 +401,15 @@
// make sure no deadlock
std::vector<std::thread> workers;
for (int key = 0;
- key < static_cast<int>(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+ key <
+ static_cast<int>(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
key++) {
workers.push_back(std::thread([key]() {
auto& allocator = MetricIdAllocator::GetInstance();
RawAddress fake_mac_address = kthAddress(key);
allocator.AllocateId(fake_mac_address);
EXPECT_TRUE(allocator.SaveDevice(fake_mac_address));
- EXPECT_TRUE(allocator.ForgetDevice(fake_mac_address));
+ allocator.ForgetDevice(fake_mac_address);
}));
}
for (auto& worker : workers) {
diff --git a/gd/Android.bp b/gd/Android.bp
index f4d0e62..6fb243f 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -143,6 +143,9 @@
generated_headers: [
"BluetoothGeneratedPackets_h",
"BluetoothFacadeGeneratedStub_h",
+ // Needed here to guarantee that generated zip file is created before
+ // bluetooth_cert_tests.zip is packaged
+ "BluetoothFacadeAndCertGeneratedStub_py",
],
generated_sources: [
"BluetoothFacadeGeneratedStub_cc",
@@ -372,6 +375,7 @@
"hci/facade/le_scanning_manager_facade.proto",
"neighbor/facade/facade.proto",
"l2cap/classic/facade.proto",
+ "l2cap/le/facade.proto",
"security/facade.proto",
],
}
@@ -407,6 +411,8 @@
"hci/facade/le_scanning_manager_facade.pb.h",
"l2cap/classic/facade.grpc.pb.h",
"l2cap/classic/facade.pb.h",
+ "l2cap/le/facade.grpc.pb.h",
+ "l2cap/le/facade.pb.h",
"neighbor/facade/facade.grpc.pb.h",
"neighbor/facade/facade.pb.h",
"security/facade.grpc.pb.h",
@@ -445,6 +451,8 @@
"hci/facade/le_scanning_manager_facade.pb.cc",
"l2cap/classic/facade.grpc.pb.cc",
"l2cap/classic/facade.pb.cc",
+ "l2cap/le/facade.grpc.pb.cc",
+ "l2cap/le/facade.pb.cc",
"neighbor/facade/facade.grpc.pb.cc",
"neighbor/facade/facade.pb.cc",
"security/facade.grpc.pb.cc",
@@ -459,24 +467,23 @@
"protoc-gen-grpc-python-plugin",
"soong_zip",
],
- cmd: "mkdir -p $(genDir)/system/bt/gd && " +
- "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(in) --grpc_out=$(genDir)/system/bt/gd --python_out=$(genDir)/system/bt/gd && " +
- "touch $(genDir)/system/bt/gd/facade/__init__.py && " +
- "touch $(genDir)/system/bt/gd/hal/__init__.py && " +
- "touch $(genDir)/system/bt/gd/hci/__init__.py && " +
- "touch $(genDir)/system/bt/gd/hci/facade/__init__.py && " +
- "touch $(genDir)/system/bt/gd/l2cap/classic/__init__.py && " +
- "touch $(genDir)/system/bt/gd/neighbor/facade/__init__.py && " +
- "touch $(genDir)/system/bt/gd/security/__init__.py && " +
- "$(location soong_zip) -C $(genDir) -D $(genDir) -o $(out)",
+ cmd: "mkdir -p $(genDir)/files && " +
+ "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(in) --grpc_out=$(genDir)/files --python_out=$(genDir)/files && " +
+ "mkdir -p $(genDir)/files/cert && " +
+ "touch $(genDir)/files/cert/__init__.py && " +
+ "touch $(genDir)/files/facade/__init__.py && " +
+ "touch $(genDir)/files/hal/__init__.py && " +
+ "touch $(genDir)/files/hci/__init__.py && " +
+ "touch $(genDir)/files/hci/facade/__init__.py && " +
+ "touch $(genDir)/files/l2cap/classic/__init__.py && " +
+ "touch $(genDir)/files/l2cap/le/__init__.py && " +
+ "touch $(genDir)/files/neighbor/facade/__init__.py && " +
+ "touch $(genDir)/files/security/__init__.py && " +
+ "$(location soong_zip) -C $(genDir)/files -D $(genDir)/files -o $(out)",
srcs: [
":BluetoothFacadeProto",
],
out: ["bluetooth_cert_generated_py.zip"],
- dist: {
- targets: ["bluetooth_stack_with_facade"],
- },
-
}
cc_defaults {
diff --git a/gd/Android.mk b/gd/Android.mk
index 966e53b..1dc9af6 100644
--- a/gd/Android.mk
+++ b/gd/Android.mk
@@ -1,46 +1,94 @@
LOCAL_PATH := $(call my-dir)
-bluetooth_cert_test_file_list := \
- $(call all-named-files-under,*.py,.) \
- $(call all-named-files-under,*.proto,cert facade hal hci/cert hci/facade l2cap/classic \
- l2cap/classic/cert neighbor/facade security) \
- cert/all_cert_testcases
+LOCAL_cert_test_sources := \
+ $(call all-named-files-under,*.py,.) \
+ cert/all_cert_testcases
+LOCAL_cert_test_sources := \
+ $(filter-out gd_cert_venv% venv%, $(LOCAL_cert_test_sources))
+LOCAL_cert_test_sources := \
+ $(addprefix $(LOCAL_PATH)/, $(LOCAL_cert_test_sources))
-bluetooth_cert_test_file_list := $(addprefix $(LOCAL_PATH)/,$(bluetooth_cert_test_file_list))
+LOCAL_host_executables := \
+ $(HOST_OUT_EXECUTABLES)/bluetooth_stack_with_facade
-bluetooth_cert_test_file_list += \
- $(HOST_OUT_EXECUTABLES)/bluetooth_stack_with_facade \
- $(HOST_OUT_SHARED_LIBRARIES)/bluetooth_packets_python3.so \
- $(HOST_OUT_SHARED_LIBRARIES)/libbase.so \
- $(HOST_OUT_SHARED_LIBRARIES)/libbluetooth_gd.so \
- $(HOST_OUT_SHARED_LIBRARIES)/libc++.so \
- $(HOST_OUT_SHARED_LIBRARIES)/libchrome.so \
- $(HOST_OUT_SHARED_LIBRARIES)/libevent-host.so \
- $(HOST_OUT_SHARED_LIBRARIES)/libgrpc++_unsecure.so \
- $(HOST_OUT_SHARED_LIBRARIES)/liblog.so \
- $(HOST_OUT_SHARED_LIBRARIES)/libz-host.so \
- $(HOST_OUT_SHARED_LIBRARIES)/libprotobuf-cpp-full.so \
- $(TARGET_OUT_EXECUTABLES)/bluetooth_stack_with_facade \
- $(TARGET_OUT_SHARED_LIBRARIES)/libbluetooth_gd.so \
- $(TARGET_OUT_SHARED_LIBRARIES)/libgrpc++_unsecure.so \
- $(HOST_OUT_NATIVE_TESTS)/root-canal/root-canal
+LOCAL_host_root_canal_executables := \
+ $(HOST_OUT_NATIVE_TESTS)/root-canal/root-canal
-bluetooth_cert_env_provider_path := \
- $(call intermediates-dir-for,PACKAGING,bluetooth_cert_test_package,HOST)/system/bt/gd/cert/environment_provider.py
+LOCAL_host_python_extension_libraries := \
+ $(HOST_OUT_SHARED_LIBRARIES)/bluetooth_packets_python3.so
-$(bluetooth_cert_env_provider_path):
- @mkdir -p $(dir $@)
- $(hide) echo "PRODUCT_DEVICE = \"$(PRODUCT_DEVICE)\"" > $@
+LOCAL_host_libraries := \
+ $(HOST_OUT_SHARED_LIBRARIES)/libbase.so \
+ $(HOST_OUT_SHARED_LIBRARIES)/libbluetooth_gd.so \
+ $(HOST_OUT_SHARED_LIBRARIES)/libc++.so \
+ $(HOST_OUT_SHARED_LIBRARIES)/libchrome.so \
+ $(HOST_OUT_SHARED_LIBRARIES)/libevent-host.so \
+ $(HOST_OUT_SHARED_LIBRARIES)/libgrpc++_unsecure.so \
+ $(HOST_OUT_SHARED_LIBRARIES)/liblog.so \
+ $(HOST_OUT_SHARED_LIBRARIES)/libz-host.so \
+ $(HOST_OUT_SHARED_LIBRARIES)/libprotobuf-cpp-full.so
-bluetooth_cert_zip_path := \
- $(call intermediates-dir-for,PACKAGING,bluetooth_cert_test_package,HOST)/bluetooth_cert_test.zip
+LOCAL_target_executables := \
+ $(TARGET_OUT_EXECUTABLES)/bluetooth_stack_with_facade
-$(bluetooth_cert_zip_path): PRIVATE_BLUETOOTH_CERT_TEST_FILE_LIST := $(bluetooth_cert_test_file_list)
+LOCAL_target_libraries := \
+ $(TARGET_OUT_SHARED_LIBRARIES)/libbluetooth_gd.so \
+ $(TARGET_OUT_SHARED_LIBRARIES)/libgrpc++_unsecure.so
-$(bluetooth_cert_zip_path): PRIVATE_BLUETOOTH_CERT_ENV_PROVIDER_PATH := $(bluetooth_cert_env_provider_path)
+bluetooth_cert_src_and_bin_zip := \
+ $(call intermediates-dir-for,PACKAGING,bluetooth_cert_src_and_bin,HOST)/bluetooth_cert_src_and_bin.zip
-$(bluetooth_cert_zip_path) : $(SOONG_ZIP) $(bluetooth_cert_env_provider_path) $(bluetooth_cert_test_file_list)
- $(hide) $(SOONG_ZIP) -d -o $@ $(addprefix -f ,$(PRIVATE_BLUETOOTH_CERT_TEST_FILE_LIST)) \
- -C $(call intermediates-dir-for,PACKAGING,bluetooth_cert_test_package,HOST) -f $(PRIVATE_BLUETOOTH_CERT_ENV_PROVIDER_PATH)
+# Assume 64-bit OS
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_cert_test_sources := $(LOCAL_cert_test_sources)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_host_executables := $(LOCAL_host_executables)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_host_root_canal_executables := $(LOCAL_host_root_canal_executables)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_host_python_extension_libraries := $(LOCAL_host_python_extension_libraries)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_host_libraries := $(LOCAL_host_libraries)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_target_executables := $(LOCAL_target_executables)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_target_libraries := $(LOCAL_target_libraries)
+$(bluetooth_cert_src_and_bin_zip): $(SOONG_ZIP) $(LOCAL_cert_test_sources) \
+ $(LOCAL_host_executables) $(LOCAL_host_root_canal_executables) $(LOCAL_host_libraries) \
+ $(LOCAL_target_executables) $(LOCAL_target_libraries) $(LOCAL_host_python_extension_libraries)
+ $(hide) $(SOONG_ZIP) -d -o $@ \
+ -C system/bt/gd $(addprefix -f ,$(PRIVATE_cert_test_sources)) \
+ -C $(HOST_OUT_EXECUTABLES) $(addprefix -f ,$(PRIVATE_host_executables)) \
+ -C $(HOST_OUT_NATIVE_TESTS)/root-canal $(addprefix -f ,$(PRIVATE_host_root_canal_executables)) \
+ -C $(HOST_OUT_SHARED_LIBRARIES) $(addprefix -f ,$(PRIVATE_host_python_extension_libraries)) \
+ -P lib64 \
+ -C $(HOST_OUT_SHARED_LIBRARIES) $(addprefix -f ,$(PRIVATE_host_libraries)) \
+ -P target \
+ -C $(TARGET_OUT_EXECUTABLES) $(addprefix -f ,$(PRIVATE_target_executables)) \
+ -C $(TARGET_OUT_SHARED_LIBRARIES) $(addprefix -f ,$(PRIVATE_target_libraries))
-$(call dist-for-goals,bluetooth_stack_with_facade,$(bluetooth_cert_zip_path):bluetooth_cert_test.zip)
+# TODO: Find a better way to locate output from SOONG genrule()
+LOCAL_cert_generated_py_zip := \
+ $(SOONG_OUT_DIR)/.intermediates/system/bt/gd/BluetoothFacadeAndCertGeneratedStub_py/gen/bluetooth_cert_generated_py.zip
+
+LOCAL_acts_zip := $(HOST_OUT)/acts-dist/acts.zip
+
+bluetooth_cert_tests_py_package_zip := \
+ $(call intermediates-dir-for,PACKAGING,bluetooth_cert_tests_py_package,HOST)/bluetooth_cert_tests.zip
+
+$(bluetooth_cert_tests_py_package_zip): PRIVATE_cert_src_and_bin_zip := $(bluetooth_cert_src_and_bin_zip)
+$(bluetooth_cert_tests_py_package_zip): PRIVATE_acts_zip := $(LOCAL_acts_zip)
+$(bluetooth_cert_tests_py_package_zip): PRIVATE_cert_generated_py_zip := $(LOCAL_cert_generated_py_zip)
+$(bluetooth_cert_tests_py_package_zip): $(SOONG_ZIP) $(LOCAL_acts_zip) \
+ $(bluetooth_cert_src_and_bin_zip) $(bluetooth_cert_generated_py_zip)
+ @echo "Packaging Bluetooth Cert Tests into $@"
+ @rm -rf $(dir $@)bluetooth_cert_tests
+ @rm -rf $(dir $@)acts
+ @mkdir -p $(dir $@)bluetooth_cert_tests
+ @mkdir -p $(dir $@)acts
+ $(hide) unzip -o -q $(PRIVATE_acts_zip) "tools/test/connectivity/acts/framework/*" -d $(dir $@)acts
+ $(hide) unzip -o -q $(PRIVATE_cert_src_and_bin_zip) -d $(dir $@)bluetooth_cert_tests
+ $(hide) unzip -o -q $(PRIVATE_cert_generated_py_zip) -d $(dir $@)bluetooth_cert_tests
+ # Make all subdirectory of gd Python pacakages except lib64 and target
+ $(hide) for f in `find $(dir $@)bluetooth_cert_tests -type d -name "*" \
+ -not -path "$(dir $@)bluetooth_cert_tests/target*" \
+ -not -path "$(dir $@)bluetooth_cert_tests/lib64*"` \
+ ; do (touch -a $$f/__init__.py) ; done
+ $(hide) $(SOONG_ZIP) -d -o $@ -C $(dir $@)bluetooth_cert_tests -D $(dir $@)bluetooth_cert_tests \
+ -P acts_framework \
+ -C $(dir $@)acts/tools/test/connectivity/acts/framework -D $(dir $@)acts/tools/test/connectivity/acts/framework
+
+$(call dist-for-goals,bluetooth_stack_with_facade,$(bluetooth_cert_tests_py_package_zip):bluetooth_cert_tests.zip)
\ No newline at end of file
diff --git a/gd/cert/all_cert_testcases b/gd/cert/all_cert_testcases
index a5bb929..e8b1d1f 100644
--- a/gd/cert/all_cert_testcases
+++ b/gd/cert/all_cert_testcases
@@ -10,3 +10,4 @@
LeAclManagerTest
StackTest
L2capTest
+LeL2capTest
diff --git a/gd/cert/android_devices_config.json b/gd/cert/android_devices_config.json
index 7123c65..1c0ec78 100644
--- a/gd/cert/android_devices_config.json
+++ b/gd/cert/android_devices_config.json
@@ -12,6 +12,7 @@
"signal_port": "8894",
"label": "cert_stack",
"serial_number": "CERT",
+ "name": "Cert Device",
"cmd":
[
"adb",
@@ -31,6 +32,7 @@
"signal_port": "8895",
"label": "stack_under_test",
"serial_number": "DUT",
+ "name": "DUT Device",
"cmd":
[
"adb",
diff --git a/gd/cert/captures.py b/gd/cert/captures.py
index 2dff151..c16afec 100644
--- a/gd/cert/captures.py
+++ b/gd/cert/captures.py
@@ -17,7 +17,7 @@
import bluetooth_packets_python3 as bt_packets
from bluetooth_packets_python3 import hci_packets
from bluetooth_packets_python3 import l2cap_packets
-from bluetooth_packets_python3.l2cap_packets import CommandCode
+from bluetooth_packets_python3.l2cap_packets import CommandCode, LeCommandCode
from cert.capture import Capture
from cert.matchers import L2capMatchers
@@ -47,6 +47,18 @@
list(packet.event)))))
+def LeConnectionCompleteCapture():
+ return Capture(lambda packet: packet.event[0] == 0x3e
+ and (packet.event[2] == 0x01 or packet.event[2] == 0x0a),
+ lambda packet: hci_packets.LeConnectionCompleteView(
+ hci_packets.LeMetaEventView(
+ hci_packets.EventPacketView(
+ bt_packets.PacketViewLittleEndian(
+ list(packet.event)
+ )
+ ))))
+
+
class L2capCaptures(object):
@staticmethod
@@ -60,3 +72,27 @@
frame = L2capMatchers.control_frame_with_code(
packet, CommandCode.CONNECTION_RESPONSE)
return l2cap_packets.ConnectionResponseView(frame)
+
+ @staticmethod
+ def CreditBasedConnectionRequest(psm):
+ return Capture(
+ L2capMatchers.CreditBasedConnectionRequest(psm),
+ L2capCaptures._extract_credit_based_connection_request)
+
+ @staticmethod
+ def _extract_credit_based_connection_request(packet):
+ frame = L2capMatchers.le_control_frame_with_code(
+ packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_REQUEST)
+ return l2cap_packets.LeCreditBasedConnectionRequestView(frame)
+
+ @staticmethod
+ def CreditBasedConnectionResponse(scid):
+ return Capture(
+ L2capMatchers.CreditBasedConnectionResponse(scid),
+ L2capCaptures._extract_credit_based_connection_response)
+
+ @staticmethod
+ def _extract_credit_based_connection_response(packet):
+ frame = L2capMatchers.le_control_frame_with_code(
+ packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_RESPONSE)
+ return l2cap_packets.LeCreditBasedConnectionResponseView(frame)
diff --git a/gd/cert/cert_self_test.py b/gd/cert/cert_self_test.py
index cec7051..5041921 100644
--- a/gd/cert/cert_self_test.py
+++ b/gd/cert/cert_self_test.py
@@ -14,18 +14,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from datetime import datetime, timedelta
import logging
import time
from mobly import asserts
-from datetime import datetime, timedelta
-from acts.base_test import BaseTestClass
-from cert.event_stream import EventStream, FilteringEventStream
-from cert.truth import assertThat
-# Test packet nesting
+from acts.base_test import BaseTestClass
+
from bluetooth_packets_python3 import hci_packets
from bluetooth_packets_python3 import l2cap_packets
+from cert.event_stream import EventStream, FilteringEventStream
+from cert.truth import assertThat
class BogusProto:
diff --git a/gd/cert/event_stream.py b/gd/cert/event_stream.py
index c8cee2f..fc9fb56 100644
--- a/gd/cert/event_stream.py
+++ b/gd/cert/event_stream.py
@@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from abc import ABC, abstractmethod
+from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timedelta
import logging
from queue import SimpleQueue, Empty
@@ -21,10 +23,8 @@
from mobly import asserts
from google.protobuf import text_format
-from concurrent.futures import ThreadPoolExecutor
-from grpc import RpcError
-from abc import ABC, abstractmethod
+from grpc import RpcError
from cert.closable import Closable
diff --git a/gd/cert/gd_base_test_facade_only.py b/gd/cert/gd_base_test.py
similarity index 89%
rename from gd/cert/gd_base_test_facade_only.py
rename to gd/cert/gd_base_test.py
index 1412e64..284ffcd 100644
--- a/gd/cert/gd_base_test_facade_only.py
+++ b/gd/cert/gd_base_test.py
@@ -14,26 +14,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from acts import asserts
-from acts.base_test import BaseTestClass
-from facade import rootservice_pb2 as facade_rootservice
-
import importlib
import logging
import os
import signal
import subprocess
-
-def is_subprocess_alive(process, timeout_seconds=1):
- try:
- process.wait(timeout=timeout_seconds)
- return False
- except subprocess.TimeoutExpired as exp:
- return True
+from acts import asserts
+from acts.base_test import BaseTestClass
+from cert.os_utils import get_gd_root, is_subprocess_alive
+from facade import rootservice_pb2 as facade_rootservice
-class GdFacadeOnlyBaseTestClass(BaseTestClass):
+class GdBaseTestClass(BaseTestClass):
def setup_class(self, dut_module, cert_module):
self.dut_module = dut_module
@@ -49,9 +42,7 @@
self.rootcanal_logs = open(rootcanal_logpath, 'w')
rootcanal_config = self.controller_configs['rootcanal']
rootcanal_hci_port = str(rootcanal_config.get("hci_port", "6402"))
- rootcanal = os.path.join(
- os.getcwd(),
- "out/host/linux-x86/nativetest64/root-canal/root-canal")
+ rootcanal = os.path.join(get_gd_root(), "root-canal")
self.rootcanal_process = subprocess.Popen(
[
rootcanal,
@@ -59,7 +50,7 @@
rootcanal_hci_port,
str(rootcanal_config.get("link_layer_port", "6403"))
],
- cwd=os.getcwd(),
+ cwd=get_gd_root(),
env=os.environ.copy(),
stdout=self.rootcanal_logs,
stderr=self.rootcanal_logs)
diff --git a/gd/cert/gd_device.py b/gd/cert/gd_device.py
index 77dbe8c..0d4c295 100644
--- a/gd/cert/gd_device.py
+++ b/gd/cert/gd_device.py
@@ -16,8 +16,11 @@
import logging
-from facade import rootservice_pb2_grpc as facade_rootservice_pb2_grpc
+from google.protobuf import empty_pb2 as empty_proto
+
from cert.gd_device_base import GdDeviceBase, replace_vars
+from cert.event_stream import EventStream
+from facade import rootservice_pb2_grpc as facade_rootservice_pb2_grpc
from hal import facade_pb2_grpc as hal_facade_pb2_grpc
from hci.facade import facade_pb2 as hci_facade
from hci.facade import facade_pb2_grpc as hci_facade_pb2_grpc
@@ -26,11 +29,10 @@
from hci.facade import le_acl_manager_facade_pb2_grpc
from hci.facade import le_advertising_manager_facade_pb2_grpc
from hci.facade import le_scanning_manager_facade_pb2_grpc
-from neighbor.facade import facade_pb2_grpc as neighbor_facade_pb2_grpc
from l2cap.classic import facade_pb2_grpc as l2cap_facade_pb2_grpc
+from l2cap.le import facade_pb2_grpc as l2cap_le_facade_pb2_grpc
+from neighbor.facade import facade_pb2_grpc as neighbor_facade_pb2_grpc
from security import facade_pb2_grpc as security_facade_pb2_grpc
-from google.protobuf import empty_pb2 as empty_proto
-from cert.event_stream import EventStream
ACTS_CONTROLLER_CONFIG_NAME = "GdDevice"
ACTS_CONTROLLER_REFERENCE_NAME = "gd_devices"
@@ -67,16 +69,17 @@
devices.append(
GdDevice(config["grpc_port"], config["grpc_root_server_port"],
config["signal_port"], resolved_cmd, config["label"],
- config.get("serial_number", "")))
+ config.get("serial_number", ""), config.get("name", "")))
return devices
class GdDevice(GdDeviceBase):
def __init__(self, grpc_port, grpc_root_server_port, signal_port, cmd,
- label, serial_number):
+ label, serial_number, name):
super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd,
- label, ACTS_CONTROLLER_CONFIG_NAME, serial_number)
+ label, ACTS_CONTROLLER_CONFIG_NAME, serial_number,
+ name)
# Facade stubs
self.rootservice = facade_rootservice_pb2_grpc.RootFacadeStub(
@@ -91,6 +94,8 @@
self.hci.send_command_with_status = self.__send_hci_command_with_status
self.l2cap = l2cap_facade_pb2_grpc.L2capClassicModuleFacadeStub(
self.grpc_channel)
+ self.l2cap_le = l2cap_le_facade_pb2_grpc.L2capLeModuleFacadeStub(
+ self.grpc_channel)
self.hci_acl_manager = acl_manager_facade_pb2_grpc.AclManagerFacadeStub(
self.grpc_channel)
self.hci_le_acl_manager = le_acl_manager_facade_pb2_grpc.LeAclManagerFacadeStub(
diff --git a/gd/cert/gd_device_base.py b/gd/cert/gd_device_base.py
index 7c00cfc..2ec0d81 100644
--- a/gd/cert/gd_device_base.py
+++ b/gd/cert/gd_device_base.py
@@ -29,12 +29,7 @@
import grpc
-from cert.environment_provider import PRODUCT_DEVICE
-from cert.gd_base_test_facade_only import is_subprocess_alive
-
-ANDROID_PRODUCT_OUT = os.path.join(
- os.getcwd(), "out/dist/bluetooth_cert_test/out/target/product",
- PRODUCT_DEVICE)
+from cert.os_utils import get_gd_root, is_subprocess_alive
WAIT_CHANNEL_READY_TIMEOUT = 10
WAIT_FOR_DEVICE_TIMEOUT = 180
@@ -49,8 +44,7 @@
rootcanal_port = ""
if serial_number == "DUT" or serial_number == "CERT":
raise Exception("Did you forget to configure the serial number?")
- android_host_out = os.path.join(os.getcwd(), "out/host/linux-x86")
- return string.replace("$ANDROID_HOST_OUT", android_host_out) \
+ return string.replace("$GD_ROOT", get_gd_root()) \
.replace("$(grpc_port)", config.get("grpc_port")) \
.replace("$(grpc_root_server_port)", config.get("grpc_root_server_port")) \
.replace("$(rootcanal_port)", rootcanal_port) \
@@ -61,7 +55,7 @@
class GdDeviceBase:
def __init__(self, grpc_port, grpc_root_server_port, signal_port, cmd,
- label, type_identifier, serial_number):
+ label, type_identifier, serial_number, name):
self.label = label if label is not None else grpc_port
# logging.log_path only exists when this is used in an ACTS test run.
self.log_path_base = context.get_current_context().get_full_output_path(
@@ -78,6 +72,10 @@
'%s_btsnoop_hci.log' % label)
cmd.append("--btsnoop=" + btsnoop_path)
+ self.grpc_root_server_port = int(grpc_root_server_port)
+ self.grpc_port = int(grpc_port)
+ self.signal_port = int(signal_port)
+
self.serial_number = serial_number
if self.serial_number:
self.adb = AdbProxy(self.serial_number)
@@ -87,35 +85,35 @@
msg="device %s cannot run as root after enabling verity" %
self.serial_number)
self.adb.shell("date " + time.strftime("%m%d%H%M%Y.%S"))
- self.adb.tcp_forward(int(grpc_port), int(grpc_port))
- self.adb.tcp_forward(
- int(grpc_root_server_port), int(grpc_root_server_port))
- self.adb.reverse("tcp:%s tcp:%s" % (signal_port, signal_port))
+ self.tcp_forward_or_die(self.grpc_port, self.grpc_port)
+ self.tcp_forward_or_die(self.grpc_root_server_port,
+ self.grpc_root_server_port)
+ self.tcp_reverse_or_die(self.signal_port, self.signal_port)
self.push_or_die(
- os.path.join(ANDROID_PRODUCT_OUT,
- "system/bin/bluetooth_stack_with_facade"),
- "system/bin")
+ os.path.join(get_gd_root(), "target",
+ "bluetooth_stack_with_facade"), "system/bin")
self.push_or_die(
- os.path.join(ANDROID_PRODUCT_OUT,
- "system/lib64/libbluetooth_gd.so"), "system/lib64")
+ os.path.join(get_gd_root(), "target", "libbluetooth_gd.so"),
+ "system/lib64")
self.push_or_die(
- os.path.join(ANDROID_PRODUCT_OUT,
- "system/lib64/libgrpc++_unsecure.so"),
+ os.path.join(get_gd_root(), "target", "libgrpc++_unsecure.so"),
"system/lib64")
self.ensure_no_output(self.adb.shell("logcat -c"))
self.adb.shell("rm /data/misc/bluetooth/logs/btsnoop_hci.log")
self.ensure_no_output(self.adb.shell("svc bluetooth disable"))
+ self.name = name
+
tester_signal_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tester_signal_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1)
- socket_address = ('localhost', int(signal_port))
+ socket_address = ('localhost', self.signal_port)
tester_signal_socket.bind(socket_address)
tester_signal_socket.listen(1)
self.backing_process = subprocess.Popen(
cmd,
- cwd=os.getcwd(),
+ cwd=get_gd_root(),
env=os.environ.copy(),
stdout=self.backing_process_logs,
stderr=self.backing_process_logs)
@@ -131,7 +129,6 @@
self.grpc_root_server_channel = grpc.insecure_channel(
"localhost:" + grpc_root_server_port)
- self.grpc_port = int(grpc_port)
self.grpc_channel = grpc.insecure_channel("localhost:" + grpc_port)
def clean_up(self):
@@ -146,6 +143,9 @@
(self.label, backing_process_return_code))
if self.serial_number:
+ self.adb.remove_tcp_forward(self.grpc_port)
+ self.adb.remove_tcp_forward(self.grpc_root_server_port)
+ self.adb.reverse("--remove tcp:%d" % self.signal_port)
self.adb.shell("logcat -d -f /data/misc/bluetooth/logs/system_log")
self.adb.pull(
"/data/misc/bluetooth/logs/btsnoop_hci.log %s" % os.path.join(
@@ -191,6 +191,44 @@
(src_file_path, dst_file_path, e),
extras=e)
+ def tcp_forward_or_die(self, host_port, device_port):
+ """
+ Forward a TCP port from host to device or fail
+ :param host_port: host port, int, 0 for adb to assign one
+ :param device_port: device port, int
+ :return: host port int
+ """
+ error_or_port = self.adb.forward(
+ "tcp:%d tcp:%d" % (host_port, device_port), ignore_status=True)
+ if not error_or_port:
+ logging.debug("host port %d was already forwarded" % host_port)
+ return host_port
+ if not isinstance(error_or_port, int):
+ asserts.fail(
+ 'Unable to forward host port %d to device port %d, error %s' %
+ (host_port, device_port, error_or_port))
+ return error_or_port
+
+ def tcp_reverse_or_die(self, device_port, host_port):
+ """
+ Forward a TCP port from device to host or fail
+ :param device_port: device port, int, 0 for adb to assign one
+ :param host_port: host port, int
+ :return: device port int
+ """
+ error_or_port = self.adb.reverse(
+ "tcp:%d tcp:%d" % (device_port, host_port))
+ if not error_or_port:
+ logging.debug("device port %d was already reversed" % device_port)
+ return device_port
+ try:
+ error_or_port = int(error_or_port)
+ except ValueError:
+ asserts.fail(
+ 'Unable to reverse device port %d to host port %d, error %s' %
+ (device_port, host_port, error_or_port))
+ return error_or_port
+
def ensure_verity_disabled(self):
"""Ensures that verity is enabled.
diff --git a/gd/cert/host_config.json b/gd/cert/host_config.json
index 9de9d70..3ffb547 100644
--- a/gd/cert/host_config.json
+++ b/gd/cert/host_config.json
@@ -18,9 +18,10 @@
"grpc_root_server_port": "8996",
"signal_port": "8994",
"label": "cert_stack",
+ "name": "Cert Device",
"cmd":
[
- "$ANDROID_HOST_OUT/bin/bluetooth_stack_with_facade",
+ "$GD_ROOT/bluetooth_stack_with_facade",
"--grpc-port=$(grpc_port)",
"--root-server-port=$(grpc_root_server_port)",
"--rootcanal-port=$(rootcanal_port)",
@@ -32,9 +33,10 @@
"grpc_root_server_port": "8997",
"signal_port": "8995",
"label": "stack_under_test",
+ "name": "DUT Device",
"cmd":
[
- "$ANDROID_HOST_OUT/bin/bluetooth_stack_with_facade",
+ "$GD_ROOT/bluetooth_stack_with_facade",
"--grpc-port=$(grpc_port)",
"--root-server-port=$(grpc_root_server_port)",
"--rootcanal-port=$(rootcanal_port)",
diff --git a/gd/cert/matchers.py b/gd/cert/matchers.py
index a30becf..805931b 100644
--- a/gd/cert/matchers.py
+++ b/gd/cert/matchers.py
@@ -16,10 +16,10 @@
import bluetooth_packets_python3 as bt_packets
from bluetooth_packets_python3 import l2cap_packets
-from bluetooth_packets_python3.l2cap_packets import CommandCode
+from bluetooth_packets_python3.l2cap_packets import CommandCode, LeCommandCode
from bluetooth_packets_python3.l2cap_packets import ConnectionResponseResult
from bluetooth_packets_python3.l2cap_packets import InformationRequestInfoType
-import logging
+from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
class L2capMatchers(object):
@@ -53,22 +53,57 @@
return lambda packet: L2capMatchers._is_control_frame_with_code(packet, CommandCode.COMMAND_REJECT)
@staticmethod
+ def LeCommandReject():
+ return lambda packet: L2capMatchers._is_le_control_frame_with_code(packet, LeCommandCode.COMMAND_REJECT)
+
+ @staticmethod
+ def CreditBasedConnectionRequest(psm):
+ return lambda packet: L2capMatchers._is_matching_credit_based_connection_request(packet, psm)
+
+ @staticmethod
+ def CreditBasedConnectionResponse(
+ scid, result=LeCreditBasedConnectionResponseResult.SUCCESS):
+ return lambda packet: L2capMatchers._is_matching_credit_based_connection_response(packet, scid, result)
+
+ @staticmethod
+ def LeDisconnectionRequest(scid, dcid):
+ return lambda packet: L2capMatchers._is_matching_le_disconnection_request(packet, scid, dcid)
+
+ @staticmethod
+ def LeDisconnectionResponse(scid, dcid):
+ return lambda packet: L2capMatchers._is_matching_le_disconnection_response(packet, scid, dcid)
+
+ @staticmethod
def SFrame(req_seq=None, f=None, s=None, p=None):
return lambda packet: L2capMatchers._is_matching_supervisory_frame(packet, req_seq, f, s, p)
@staticmethod
- def IFrame(tx_seq=None, payload=None):
- return lambda packet: L2capMatchers._is_matching_information_frame(packet, tx_seq, payload)
+ def IFrame(tx_seq=None, payload=None, f=None):
+ return lambda packet: L2capMatchers._is_matching_information_frame(packet, tx_seq, payload, f)
@staticmethod
def Data(payload):
return lambda packet: packet.GetPayload().GetBytes() == payload
+ @staticmethod
+ def FirstLeIFrame(payload, sdu_size):
+ return lambda packet: L2capMatchers._is_matching_first_le_i_frame(packet, payload, sdu_size)
+
# this is a hack - should be removed
@staticmethod
def PartialData(payload):
return lambda packet: payload in packet.GetPayload().GetBytes()
+ # this is a hack - should be removed
+ @staticmethod
+ def PacketPayloadRawData(payload):
+ return lambda packet: payload in packet.payload
+
+ # this is a hack - should be removed
+ @staticmethod
+ def PacketPayloadWithMatchingPsm(psm):
+ return lambda packet: None if psm != packet.psm else packet
+
@staticmethod
def ExtractBasicFrame(scid):
return lambda packet: L2capMatchers._basic_frame_for(packet, scid)
@@ -109,7 +144,7 @@
return l2cap_packets.EnhancedSupervisoryFrameView(standard_frame)
@staticmethod
- def _is_matching_information_frame(packet, tx_seq, payload):
+ def _is_matching_information_frame(packet, tx_seq, payload, f):
frame = L2capMatchers._information_frame(packet)
if frame is None:
return False
@@ -117,6 +152,8 @@
return False
if payload is not None and frame.GetPayload().GetBytes() != payload:
return False
+ if f is not None and frame.GetF() != f:
+ return False
return True
@staticmethod
@@ -135,12 +172,24 @@
return True
@staticmethod
+ def _is_matching_first_le_i_frame(packet, payload, sdu_size):
+ first_le_i_frame = l2cap_packets.FirstLeInformationFrameView(packet)
+ return first_le_i_frame.GetPayload().GetBytes(
+ ) == payload and first_le_i_frame.GetL2capSduLength() == sdu_size
+
+ @staticmethod
def _control_frame(packet):
if packet.GetChannelId() != 1:
return None
return l2cap_packets.ControlView(packet.GetPayload())
@staticmethod
+ def _le_control_frame(packet):
+ if packet.GetChannelId() != 5:
+ return None
+ return l2cap_packets.LeControlView(packet.GetPayload())
+
+ @staticmethod
def control_frame_with_code(packet, code):
frame = L2capMatchers._control_frame(packet)
if frame is None or frame.GetCode() != code:
@@ -148,10 +197,22 @@
return frame
@staticmethod
+ def le_control_frame_with_code(packet, code):
+ frame = L2capMatchers._le_control_frame(packet)
+ if frame is None or frame.GetCode() != code:
+ return None
+ return frame
+
+ @staticmethod
def _is_control_frame_with_code(packet, code):
return L2capMatchers.control_frame_with_code(packet, code) is not None
@staticmethod
+ def _is_le_control_frame_with_code(packet, code):
+ return L2capMatchers.le_control_frame_with_code(packet,
+ code) is not None
+
+ @staticmethod
def _is_matching_connection_response(packet, scid):
frame = L2capMatchers.control_frame_with_code(
packet, CommandCode.CONNECTION_RESPONSE)
@@ -173,6 +234,26 @@
) == dcid
@staticmethod
+ def _is_matching_le_disconnection_response(packet, scid, dcid):
+ frame = L2capMatchers.le_control_frame_with_code(
+ packet, LeCommandCode.DISCONNECTION_RESPONSE)
+ if frame is None:
+ return False
+ response = l2cap_packets.LeDisconnectionResponseView(frame)
+ return response.GetSourceCid() == scid and response.GetDestinationCid(
+ ) == dcid
+
+ @staticmethod
+ def _is_matching_le_disconnection_request(packet, scid, dcid):
+ frame = L2capMatchers.le_control_frame_with_code(
+ packet, LeCommandCode.DISCONNECTION_REQUEST)
+ if frame is None:
+ return False
+ request = l2cap_packets.LeDisconnectionRequestView(frame)
+ return request.GetSourceCid() == scid and request.GetDestinationCid(
+ ) == dcid
+
+ @staticmethod
def _information_response_with_type(packet, info_type):
frame = L2capMatchers.control_frame_with_code(
packet, CommandCode.INFORMATION_RESPONSE)
@@ -203,3 +284,23 @@
) != supports_fixed_channels:
return False
return True
+
+ @staticmethod
+ def _is_matching_credit_based_connection_request(packet, psm):
+ frame = L2capMatchers.le_control_frame_with_code(
+ packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_REQUEST)
+ if frame is None:
+ return False
+ request = l2cap_packets.LeCreditBasedConnectionRequestView(frame)
+ return request.GetLePsm() == psm
+
+ @staticmethod
+ def _is_matching_credit_based_connection_response(packet, scid, result):
+ frame = L2capMatchers.le_control_frame_with_code(
+ packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_RESPONSE)
+ if frame is None:
+ return False
+ response = l2cap_packets.LeCreditBasedConnectionResponseView(frame)
+ return response.GetResult() == result and (
+ result != LeCreditBasedConnectionResponseResult.SUCCESS or
+ response.GetDestinationCid() != 0)
diff --git a/gd/cert/os_utils.py b/gd/cert/os_utils.py
new file mode 100644
index 0000000..45e32f9
--- /dev/null
+++ b/gd/cert/os_utils.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+from pathlib import Path
+import subprocess
+
+
+def is_subprocess_alive(process, timeout_seconds=1):
+ """
+ Check if a process is alive for at least timeout_seconds
+ :param process: a Popen object that represent a subprocess
+ :param timeout_seconds: process needs to be alive for at least
+ timeout_seconds
+ :return: True if process is alive for at least timeout_seconds
+ """
+ try:
+ process.wait(timeout=timeout_seconds)
+ return False
+ except subprocess.TimeoutExpired as exp:
+ return True
+
+
+def get_gd_root():
+ """
+ Return the root of the GD test library
+
+ GD root is the parent directory of cert
+ :return: root directory string of gd test library
+ """
+ return str(Path(__file__).absolute().parents[1])
diff --git a/gd/cert/pts.json b/gd/cert/pts.json
index 76a27be..b266c5a 100644
--- a/gd/cert/pts.json
+++ b/gd/cert/pts.json
@@ -13,6 +13,7 @@
"signal_port": "8895",
"label": "stack_under_test",
"serial_number": "DUT",
+ "name": "Cert Device",
"cmd":
[
"adb",
diff --git a/gd/cert/pts_base_test.py b/gd/cert/pts_base_test.py
index 906bccf..2b1b555 100644
--- a/gd/cert/pts_base_test.py
+++ b/gd/cert/pts_base_test.py
@@ -17,10 +17,6 @@
from acts.base_test import BaseTestClass
import importlib
-import logging
-import os
-import signal
-import subprocess
class PTSBaseTestClass(BaseTestClass):
diff --git a/gd/cert/py_acl_manager.py b/gd/cert/py_acl_manager.py
index aa3e810..d193a8d 100644
--- a/gd/cert/py_acl_manager.py
+++ b/gd/cert/py_acl_manager.py
@@ -16,16 +16,12 @@
from google.protobuf import empty_pb2 as empty_proto
from cert.event_stream import EventStream
-from cert.event_stream import FilteringEventStream
from cert.event_stream import IEventStream
-from cert.captures import ReadBdAddrCompleteCapture
from cert.captures import ConnectionCompleteCapture
-from cert.captures import ConnectionRequestCapture
from cert.closable import Closable
from cert.closable import safeClose
from bluetooth_packets_python3 import hci_packets
from cert.truth import assertThat
-from hci.facade import facade_pb2 as hci_facade
from hci.facade import acl_manager_facade_pb2 as acl_manager_facade
diff --git a/gd/cert/py_l2cap.py b/gd/cert/py_l2cap.py
index 0246dfa..f26e0c9 100644
--- a/gd/cert/py_l2cap.py
+++ b/gd/cert/py_l2cap.py
@@ -14,8 +14,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from google.protobuf import empty_pb2 as empty_proto
+
from l2cap.classic import facade_pb2 as l2cap_facade_pb2
+from l2cap.le import facade_pb2 as l2cap_le_facade_pb2
from bluetooth_packets_python3 import l2cap_packets
+from cert.event_stream import FilteringEventStream
+from cert.event_stream import EventStream, IEventStream
+from cert.closable import Closable, safeClose
+from cert.truth import assertThat
+from cert.matchers import L2capMatchers
+from facade import common_pb2 as common
class PyL2capChannel(object):
@@ -29,11 +38,14 @@
l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=payload))
-class PyL2cap(object):
+class PyL2cap(Closable):
def __init__(self, device):
self._device = device
+ def close(self):
+ pass
+
def open_channel(self,
psm=0x33,
mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC):
@@ -43,3 +55,76 @@
l2cap_facade_pb2.SetEnableDynamicChannelRequest(
psm=psm, retransmission_mode=mode))
return PyL2capChannel(self._device, psm)
+
+
+class PyLeL2capChannel(IEventStream):
+
+ def __init__(self, device, psm, l2cap_stream):
+ self._device = device
+ self._psm = psm
+ self._le_l2cap_stream = l2cap_stream
+ self._our_le_l2cap_view = FilteringEventStream(
+ self._le_l2cap_stream,
+ L2capMatchers.PacketPayloadWithMatchingPsm(self._psm))
+
+ def get_event_queue(self):
+ return self._our_le_l2cap_view.get_event_queue()
+
+ def send(self, payload):
+ self._device.l2cap_le.SendDynamicChannelPacket(
+ l2cap_le_facade_pb2.DynamicChannelPacket(psm=0x33, payload=payload))
+
+
+class CreditBasedConnectionResponseFutureWrapper(object):
+ """
+ The future object returned when we send a connection request from DUT. Can be used to get connection status and
+ create the corresponding PyLeL2capChannel object later
+ """
+
+ def __init__(self, grpc_response_future, device, psm, le_l2cap_stream):
+ self._grpc_response_future = grpc_response_future
+ self._device = device
+ self._psm = psm
+ self._le_l2cap_stream = le_l2cap_stream
+
+ def get_status(self):
+ return l2cap_packets.LeCreditBasedConnectionResponseResult(
+ self._grpc_response_future.result().status)
+
+ def get_channel(self):
+ assertThat(self.get_status()).isEqualTo(
+ l2cap_packets.LeCreditBasedConnectionResponseResult.SUCCESS)
+ return PyLeL2capChannel(self._device, self._psm, self._le_l2cap_stream)
+
+
+class PyLeL2cap(Closable):
+
+ def __init__(self, device):
+ self._device = device
+ self._le_l2cap_stream = EventStream(
+ self._device.l2cap_le.FetchL2capData(empty_proto.Empty()))
+
+ def close(self):
+ safeClose(self._le_l2cap_stream)
+
+ def register_coc(self, psm=0x33):
+ self._device.l2cap_le.SetDynamicChannel(
+ l2cap_le_facade_pb2.SetEnableDynamicChannelRequest(
+ psm=psm, enable=True))
+ return PyLeL2capChannel(self._device, psm, self._le_l2cap_stream)
+
+ def connect_coc_to_cert(self, psm=0x33):
+ """
+ Send open LE COC request to CERT. Get a future for connection result, to be used after CERT accepts request
+ """
+ self.register_coc(psm)
+ # TODO: Update CERT device random address in ACL manager
+ response_future = self._device.l2cap_le.OpenDynamicChannel.future(
+ l2cap_le_facade_pb2.OpenDynamicChannelRequest(
+ psm=psm,
+ remote=common.BluetoothAddressWithType(
+ address=common.BluetoothAddress(
+ address=b"22:33:ff:ff:11:00"))))
+
+ return CreditBasedConnectionResponseFutureWrapper(
+ response_future, self._device, psm, self._le_l2cap_stream)
diff --git a/gd/cert/py_le_acl_manager.py b/gd/cert/py_le_acl_manager.py
new file mode 100644
index 0000000..a7ee3d0
--- /dev/null
+++ b/gd/cert/py_le_acl_manager.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+from google.protobuf import empty_pb2 as empty_proto
+
+from cert.event_stream import EventStream
+from cert.event_stream import IEventStream
+from cert.captures import ConnectionCompleteCapture
+from cert.captures import LeConnectionCompleteCapture
+from cert.closable import Closable
+from cert.closable import safeClose
+from bluetooth_packets_python3 import hci_packets
+from cert.truth import assertThat
+from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
+
+
+class PyLeAclManagerAclConnection(IEventStream, Closable):
+
+ def __init__(self, device, acl_stream, remote_addr, handle):
+ """
+ An abstract representation for an LE ACL connection in GD certification test
+ :param device: The GD device
+ :param acl_stream: The ACL stream for this connection
+ :param remote_addr: Remote device address
+ :param handle: Connection handle
+ """
+ self.device = device
+ self.handle = handle
+ # todo enable filtering after sorting out handles
+ #self.our_acl_stream = FilteringEventStream(acl_stream, None)
+ self.our_acl_stream = acl_stream
+
+ if remote_addr:
+ remote_addr_bytes = bytes(
+ remote_addr,
+ 'utf8') if type(remote_addr) is str else bytes(remote_addr)
+ self.connection_event_stream = EventStream(
+ self.device.hci_le_acl_manager.CreateConnection(
+ le_acl_manager_facade.LeConnectionMsg(
+ address_type=int(
+ hci_packets.AddressType.RANDOM_DEVICE_ADDRESS),
+ address=remote_addr_bytes)))
+ else:
+ self.connection_event_stream = None
+
+ def close(self):
+ safeClose(self.connection_event_stream)
+
+ def wait_for_connection_complete(self):
+ connection_complete = LeConnectionCompleteCapture()
+ assertThat(self.connection_event_stream).emits(connection_complete)
+ self.handle = connection_complete.get().GetConnectionHandle()
+
+ def send(self, data):
+ self.device.hci_le_acl_manager.SendAclData(
+ le_acl_manager_facade.LeAclData(
+ handle=self.handle, payload=bytes(data)))
+
+ def get_event_queue(self):
+ return self.our_acl_stream.get_event_queue()
+
+
+class PyLeAclManager(Closable):
+
+ def __init__(self, device):
+ """
+ LE ACL Manager for GD Certification test
+ :param device: The GD device
+ """
+ self.device = device
+
+ self.le_acl_stream = EventStream(
+ self.device.hci_le_acl_manager.FetchAclData(empty_proto.Empty()))
+ self.incoming_connection_stream = None
+
+ def close(self):
+ safeClose(self.le_acl_stream)
+ safeClose(self.incoming_connection_stream)
+
+ # temporary, until everyone is migrated
+ def get_le_acl_stream(self):
+ return self.le_acl_stream
+
+ def listen_for_incoming_connections(self):
+ self.incoming_connection_stream = EventStream(
+ self.device.hci_le_acl_manager.FetchIncomingConnection(
+ empty_proto.Empty()))
+
+ def initiate_connection(self, remote_addr):
+ return PyLeAclManagerAclConnection(self.device, self.le_acl_stream,
+ remote_addr, None)
+
+ def accept_connection(self):
+ connection_complete = ConnectionCompleteCapture()
+ assertThat(self.incoming_connection_stream).emits(connection_complete)
+ handle = connection_complete.get().GetConnectionHandle()
+ return PyLeAclManagerAclConnection(self.device, self.le_acl_stream,
+ None, handle)
diff --git a/gd/cert/python3.8-gd b/gd/cert/python3.8-gd
deleted file mode 100755
index 6aa24b2..0000000
--- a/gd/cert/python3.8-gd
+++ /dev/null
@@ -1,4 +0,0 @@
-#! /bin/bash
-BLUETOOTH_CERT_TEST_ENV=$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test_env
-PYTHONPATH=$BLUETOOTH_CERT_TEST_ENV/out/host/linux-x86/lib64:$BLUETOOTH_CERT_TEST_ENV/system/bt/gd:$PYTHONPATH \
- python3.8 "$@"
diff --git a/gd/cert/run b/gd/cert/run
index 9634691..afa5057 100755
--- a/gd/cert/run
+++ b/gd/cert/run
@@ -12,6 +12,7 @@
TEST_CONFIG="$ANDROID_BUILD_TOP/system/bt/gd/cert/android_devices_config.json"
TEST_FILTER="-tf $ANDROID_BUILD_TOP/system/bt/gd/cert/all_cert_testcases"
+REUSE_VENV=false
POSITIONAL=()
while [[ $# -gt 0 ]]
@@ -26,6 +27,19 @@
TEST_CONFIG=$ANDROID_BUILD_TOP/system/bt/gd/cert/host_config.json
shift # past argument
;;
+ --test_file=*)
+ TEST_FILTER="-tc ${key#*=}"
+ shift # past argument
+ ;;
+ --test_config=*)
+ TEST_CONFIG="${key#*=}"
+ shift # past argument
+ ;;
+ # This will speed up the test by overwriting existing venv
+ --reuse_venv)
+ REUSE_VENV=true
+ shift # past argument
+ ;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
@@ -34,21 +48,40 @@
done
set -- "${POSITIONAL[@]}" # restore positional parameters
-BLUETOOTH_CERT_TEST_ENV=$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test_env
-rm -rf BLUETOOTH_CERT_TEST_ENV
-mkdir -p $BLUETOOTH_CERT_TEST_ENV
+CERT_TEST_VENV=$ANDROID_BUILD_TOP/out/dist/bluetooth_venv
-unzip -o -q $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip \
- -d $BLUETOOTH_CERT_TEST_ENV
+if [ "$REUSE_VENV" != true ] ; then
+ rm -rf $CERT_TEST_VENV
+fi
-unzip -o -q $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test.zip \
- -d $BLUETOOTH_CERT_TEST_ENV
+python3.8 -m virtualenv --python `which python3.8` $CERT_TEST_VENV
+if [[ $? -ne 0 ]] ; then
+ echo "Error setting up virtualenv"
+ return 1
+fi
-pushd .
-cd $ANDROID_BUILD_TOP
-PYTHONPATH=$BLUETOOTH_CERT_TEST_ENV/out/host/linux-x86/lib64:$BLUETOOTH_CERT_TEST_ENV/system/bt/gd:$PYTHONPATH \
- python3.8 `which act.py`\
- -c $TEST_CONFIG \
- $TEST_FILTER \
- -tp $ANDROID_BUILD_TOP/system/bt/gd
-popd
\ No newline at end of file
+unzip -o -q $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_tests.zip -d $CERT_TEST_VENV/acts
+if [[ $? -ne 0 ]] ; then
+ echo "Error unzipping bluetooth_cert_tests.zip"
+ return 1
+fi
+
+$CERT_TEST_VENV/bin/python $CERT_TEST_VENV/acts/setup.py install
+if [[ $? -ne 0 ]] ; then
+ echo "Error installing GD libraries"
+ return 1
+fi
+
+$CERT_TEST_VENV/bin/python -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+if [[ $? -ne 0 ]] ; then
+ echo "Setup failed as bluetooth_packets_python3 cannot be imported"
+ return 1
+fi
+
+$CERT_TEST_VENV/bin/python $CERT_TEST_VENV/bin/act.py \
+ -c $TEST_CONFIG \
+ $TEST_FILTER \
+ -tp $CERT_TEST_VENV/acts
diff --git a/gd/cert/run_pts_l2cap.sh b/gd/cert/run_pts_l2cap.sh
index 92fc7cd..356b31f 100755
--- a/gd/cert/run_pts_l2cap.sh
+++ b/gd/cert/run_pts_l2cap.sh
@@ -1,9 +1,5 @@
#! /bin/bash
-unzip -u $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip -d $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py
-
-# For bluetooth_packets_python3
-pushd .
-cd $ANDROID_BUILD_TOP
-python3.8-gd `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/cert/pts.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/pts_l2cap_testcase -tp $ANDROID_BUILD_TOP/system/bt/gd
-popd
\ No newline at end of file
+source $ANDROID_BUILD_TOP/system/bt/cert/run \
+ --test_config=$ANDROID_BUILD_TOP/system/bt/gd/cert/pts.json \
+ --test_file=$ANDROID_BUILD_TOP/system/bt/gd/cert/pts_l2cap_testcase
\ No newline at end of file
diff --git a/gd/cert/set_up_acts.sh b/gd/cert/set_up_acts.sh
deleted file mode 100755
index 60bfdd2..0000000
--- a/gd/cert/set_up_acts.sh
+++ /dev/null
@@ -1,109 +0,0 @@
-#! /bin/bash
-#
-# Script to setup environment to execute bluetooth certification stack
-#
-# for more info, see go/acts
-
-## Android build main build setup script relative to top level android source root
-BUILD_SETUP=./build/envsetup.sh
-
-function UsageAndroidTree {
- cat<<EOF
-Ensure invoked from within the android source tree
-EOF
-}
-
-function UsageSourcedNotExecuted {
- cat<<EOF
-Ensure script is SOURCED and not executed to persist the build setup
-e.g.
-source $0
-EOF
-}
-
-function UpFind {
- while [[ $PWD != / ]] ; do
- rc=$(find "$PWD" -maxdepth 1 "$@")
- if [ -n "$rc" ]; then
- echo $(dirname "$rc")
- return
- fi
- cd ..
- done
-}
-
-function SetUpAndroidBuild {
- pushd .
- android_root=$(UpFind -name out -type d)
- if [[ -z $android_root ]] ; then
- UsageAndroidTree
- return
- fi
- echo "Found android root $android_root"
- cd $android_root && . $BUILD_SETUP
- echo "Sourced build setup rules"
- cd $android_root && lunch
- popd
-}
-
-function SetupPython38 {
- echo "Setting up python3.8"
- sudo apt-get install python3.8-dev
-}
-
-function CompileBluetoothPacketsPython3 {
- echo "bluetooth_packets_python3 is not found, compiling"
- croot
- make -j bluetooth_packets_python3
-}
-
-if [[ "${BASH_SOURCE[0]}" == "${0}" ]] ; then
- UsageSourcedNotExecuted
- return 1
-fi
-
-if [[ -z "$ANDROID_BUILD_TOP" ]] ; then
- SetUpAndroidBuild
-fi
-
-## Check python3.8 is installed properly
-## Need Python 3.8 because bluetooth_packets_python3 is compiled against
-## Python 3.8 headers
-dpkg -l python3.8-dev > /dev/null 2>&1
-if [[ $? -ne 0 ]] ; then
- SetupPython38
-fi
-
-## Check bluetooth_packets_python3 is compiled succssfully
-PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64 python3.8 -c "
-import bluetooth_packets_python3 as bp3
-bp3.BaseStruct
-"
-if [[ $? -ne 0 ]] ; then
- pushd .
- CompileBluetoothPacketsPython3
- popd
- python3.8 -c "
-import bluetooth_packets_python3 as bp3
-bp3.BaseStruct
-"
- if [[ $? -ne 0 ]] ; then
- echo "Setup failed as bluetooth_packets_python3 cannot be found"
- else
- echo "Found bluetooth_packets_python3 after compilation"
- fi
-else
- echo "Found bluetooth_packets_python3"
-fi
-
-## All is good now so go ahead with the acts setup
-pushd .
-cd $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/
-sudo python3.8 setup.py develop
-if [[ $? -eq 0 ]] ; then
- echo "cert setup complete"
-else
- echo "cert setup failed"
-fi
-popd
-
diff --git a/gd/cert/set_up_virtualenv.sh b/gd/cert/set_up_virtualenv.sh
index 50f3d48..0c07691 100644
--- a/gd/cert/set_up_virtualenv.sh
+++ b/gd/cert/set_up_virtualenv.sh
@@ -5,9 +5,9 @@
# Usage
# 1. cd system/bt/gd
# 2. source cert/set_up_virtualenv.sh
-# 3. source gd_cert_venv/bin/activate
-# 4. [run tests, do development, hack]
-# 5. deactivate (or just close the terminal window)
+# 4. [run tests, do development, hack] using vevn/bin/python
+#
+# Note: Just use the virtualized Python binary, no need to activate
## Android build main build setup script relative to top level android source root
BUILD_SETUP=./build/envsetup.sh
@@ -61,12 +61,6 @@
sudo apt-get install python3-pip
}
-function CompileBluetoothPacketsPython3 {
- echo "bluetooth_packets_python3 is not found, compiling"
- croot
- make -j bluetooth_packets_python3
-}
-
# Deactivate existing virtual environment, if any, ignore errors
deactivate > /dev/null 2>&1
@@ -102,120 +96,49 @@
SetUpAndroidBuild
fi
-## Check bluetooth_packets_python3 is compiled succssfully
-$ANDROID_BUILD_TOP/system/bt/gd/cert/python3.8-gd -c "
-import bluetooth_packets_python3 as bp3
-bp3.BaseStruct
-"
-if [[ $? -ne 0 ]] ; then
- pushd .
- CompileBluetoothPacketsPython3
- popd
- $ANDROID_BUILD_TOP/system/bt/gd/cert/python3.8-gd -c "
-import bluetooth_packets_python3 as bp3
-bp3.BaseStruct
-"
- if [[ $? -ne 0 ]] ; then
- echo "Setup failed as bluetooth_packets_python3 cannot be found"
- return 1
- else
- echo "Found bluetooth_packets_python3 after compilation"
- fi
-else
- echo "Found bluetooth_packets_python3"
-fi
-
## Compile and unzip test artifacts
-if [[ ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip" || ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test.zip" ]]; then
- echo "bluetooth_cert_generated_py.zip OR bluetooth_cert_test.zip is not found, compiling"
- m -j dist bluetooth_stack_with_facade
- if [[ ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip" || ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test.zip" ]]; then
- echo "Failed to compile bluetooth_stack_with_facade"
- return 1
- fi
-fi
-unzip -u $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip -d $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py
+echo "Compiling bluetooth_stack_with_facade ..."
+$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="$(pwd)" dist bluetooth_stack_with_facade
if [[ $? -ne 0 ]] ; then
- echo "Failed to unzip bluetooth_cert_generated_py.zip"
+ echo "Failed to compile bluetooth_stack_with_facade"
return 1
fi
-unzip -u $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test.zip -d $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test
-if [[ $? -ne 0 ]] ; then
- echo "Failed to unzip bluetooth_cert_test.zip"
+if [[ ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_tests.zip" ]]; then
+ echo "Cannot find bluetooth_cert_tests.zip after compilation"
return 1
fi
-# Set-up virtualenv
-pushd .
-cd $ANDROID_BUILD_TOP/system/bt/gd
-virtualenv -p python3.8 gd_cert_venv
+CERT_TEST_VENV=$ANDROID_BUILD_TOP/out/dist/bluetooth_venv
+
+rm -rf $CERT_TEST_VENV
+
+python3.8 -m virtualenv --python `which python3.8` $CERT_TEST_VENV
if [[ $? -ne 0 ]] ; then
echo "Error setting up virtualenv"
- popd
return 1
fi
-popd
-# Set up artifacts
-pushd .
-cd $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/lib/python3.8/site-packages
-# Python generated code
-ln -sfT $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/acts acts
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/system/bt/gd/cert cert
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/system/bt/gd/facade facade
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/system/bt/gd/hal hal
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/system/bt/gd/hci hci
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/system/bt/gd/l2cap l2cap
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/system/bt/gd/neighbor neighbor
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/system/bt/gd/security security
-# Native libraries
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/out/host/linux-x86/lib64/bluetooth_packets_python3.so bluetooth_packets_python3.so
-# Per systrace, Python only load from python3.8/lib64 directory for plugin imported native libraries
-mkdir -p $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/lib/python3.8/lib64
-cd $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/lib/python3.8/lib64
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/out/host/linux-x86/lib64/libc++.so libc++.so
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/out/host/linux-x86/lib64/libbluetooth_gd.so libbluetooth_gd.so
-ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/out/host/linux-x86/lib64/libgrpc++_unsecure.so libgrpc++_unsecure.so
-# Binaries
-cd $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/bin
-ln -sfT $ANDROID_BUILD_TOP/out/host/linux-x86/bin/bluetooth_stack_with_facade bluetooth_stack_with_facade
-ln -sfT $ANDROID_BUILD_TOP/out/host/linux-x86/nativetest64/root-canal/root-canal root-canal
-popd
-
-# Activate virtualenv
-pushd .
-source $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/bin/activate
+unzip -o -q $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_tests.zip -d $CERT_TEST_VENV/acts
if [[ $? -ne 0 ]] ; then
- echo "Failed to activate virtualenv"
- deactivate
- popd
- return 1
-fi
-popd
-if [[ -z "$ANDROID_BUILD_TOP" ]] ; then
- echo "Failed to inherit Android build environment"
- deactivate
+ echo "Error unzipping bluetooth_cert_tests.zip"
return 1
fi
-## Set up ACTS
-# sudo is no longer needed since we are in a virtual environment
-python3.8 $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/setup.py develop
+$CERT_TEST_VENV/bin/python $CERT_TEST_VENV/acts/setup.py install
if [[ $? -ne 0 ]] ; then
- echo "ACTS setup failed"
- deactivate
+ echo "Error installing GD libraries"
return 1
fi
-pip3 install protobuf
+$CERT_TEST_VENV/bin/python -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
if [[ $? -ne 0 ]] ; then
- echo "Failed to install protobuf"
- deactivate
- return 1
+ echo "Setup failed as bluetooth_packets_python3 cannot be imported"
+ return 1
fi
-deactivate
-
echo ""
echo "Please mark GD root directory as \"Project Sources and Headers\" in IDE"
echo "If still seeing errors, invalidate cached and restart"
diff --git a/gd/cert/truth.py b/gd/cert/truth.py
index c1ea1c0..d1dfb7a 100644
--- a/gd/cert/truth.py
+++ b/gd/cert/truth.py
@@ -14,21 +14,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import time
from datetime import timedelta
from mobly.asserts import assert_true
from mobly.asserts import assert_false
-
from mobly import signals
+
from cert.event_stream import IEventStream
from cert.event_stream import NOT_FOR_YOU_assert_event_occurs
from cert.event_stream import NOT_FOR_YOU_assert_all_events_occur
from cert.event_stream import NOT_FOR_YOU_assert_none_matching
from cert.event_stream import NOT_FOR_YOU_assert_none
-import sys, traceback
-
class ObjectSubject(object):
diff --git a/gd/facade/grpc_root_server.cc b/gd/facade/grpc_root_server.cc
index 6a8e5de..7e37b5b 100644
--- a/gd/facade/grpc_root_server.cc
+++ b/gd/facade/grpc_root_server.cc
@@ -32,6 +32,7 @@
#include "hci/le_advertising_manager.h"
#include "hci/le_scanning_manager.h"
#include "l2cap/classic/facade.h"
+#include "l2cap/le/facade.h"
#include "neighbor/connectability.h"
#include "neighbor/discoverability.h"
#include "neighbor/facade/facade.h"
@@ -88,9 +89,11 @@
break;
case BluetoothModule::L2CAP:
modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+ modules.add<::bluetooth::hci::facade::LeAdvertisingManagerFacadeModule>();
modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>();
modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
modules.add<::bluetooth::l2cap::classic::L2capClassicModuleFacadeModule>();
+ modules.add<::bluetooth::l2cap::le::L2capLeModuleFacadeModule>();
modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
break;
case BluetoothModule::SECURITY:
diff --git a/gd/hal/cert/simple_hal_test.py b/gd/hal/cert/simple_hal_test.py
index 91f2faf..6b053e2 100644
--- a/gd/hal/cert/simple_hal_test.py
+++ b/gd/hal/cert/simple_hal_test.py
@@ -16,7 +16,7 @@
from datetime import timedelta
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from cert.truth import assertThat
from google.protobuf import empty_pb2
@@ -26,7 +26,7 @@
import bluetooth_packets_python3 as bt_packets
-class SimpleHalTest(GdFacadeOnlyBaseTestClass):
+class SimpleHalTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='HAL', cert_module='HAL')
diff --git a/gd/hci/acl_connection_interface.h b/gd/hci/acl_connection_interface.h
new file mode 100644
index 0000000..ae22e29
--- /dev/null
+++ b/gd/hci/acl_connection_interface.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/hci_packets.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+class AclConnectionInterface {
+ public:
+ AclConnectionInterface() = default;
+ virtual ~AclConnectionInterface() = default;
+ DISALLOW_COPY_AND_ASSIGN(AclConnectionInterface);
+
+ virtual void EnqueueCommand(std::unique_ptr<ConnectionManagementCommandBuilder> command,
+ common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
+
+ virtual void EnqueueCommand(std::unique_ptr<ConnectionManagementCommandBuilder> command,
+ common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
+
+ static constexpr EventCode AclConnectionEvents[] = {
+ EventCode::CONNECTION_PACKET_TYPE_CHANGED,
+ EventCode::ROLE_CHANGE,
+ EventCode::CONNECTION_COMPLETE,
+ EventCode::DISCONNECTION_COMPLETE,
+ EventCode::CONNECTION_REQUEST,
+ EventCode::CONNECTION_PACKET_TYPE_CHANGED,
+ EventCode::AUTHENTICATION_COMPLETE,
+ EventCode::READ_CLOCK_OFFSET_COMPLETE,
+ EventCode::MODE_CHANGE,
+ EventCode::QOS_SETUP_COMPLETE,
+ EventCode::ROLE_CHANGE,
+ EventCode::FLOW_SPECIFICATION_COMPLETE,
+ EventCode::FLUSH_OCCURRED,
+ EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE,
+ EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE,
+ EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE,
+ EventCode::ENCRYPTION_CHANGE,
+ EventCode::LINK_SUPERVISION_TIMEOUT_CHANGED,
+ };
+};
+} // namespace hci
+} // namespace bluetooth
diff --git a/gd/hci/acl_manager.cc b/gd/hci/acl_manager.cc
index 892bcf3..50cb130 100644
--- a/gd/hci/acl_manager.cc
+++ b/gd/hci/acl_manager.cc
@@ -26,6 +26,7 @@
#include "hci/acl_fragmenter.h"
#include "hci/controller.h"
#include "hci/hci_layer.h"
+#include "security/security_module.h"
namespace bluetooth {
namespace hci {
@@ -154,7 +155,7 @@
}
};
-struct AclManager::impl {
+struct AclManager::impl : public security::ISecurityManagerListener {
impl(const AclManager& acl_manager) : acl_manager_(acl_manager) {}
void Start() {
@@ -212,8 +213,6 @@
hci_layer_->RegisterEventHandler(EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE,
Bind(&impl::on_read_remote_version_information_complete, common::Unretained(this)),
handler_);
- hci_layer_->RegisterEventHandler(EventCode::ENCRYPTION_CHANGE,
- Bind(&impl::on_encryption_change, common::Unretained(this)), handler_);
hci_layer_->RegisterEventHandler(EventCode::LINK_SUPERVISION_TIMEOUT_CHANGED,
Bind(&impl::on_link_supervision_timeout_changed, common::Unretained(this)),
handler_);
@@ -237,6 +236,7 @@
hci_queue_end_ = nullptr;
handler_ = nullptr;
hci_layer_ = nullptr;
+ security_manager_.reset();
}
void incoming_acl_credits(uint16_t handle, uint16_t credits) {
@@ -588,8 +588,11 @@
}
}
- void on_encryption_change(EventPacketView packet) {
- EncryptionChangeView encryption_change_view = EncryptionChangeView::Create(packet);
+ void OnDeviceBonded(bluetooth::hci::AddressWithType device) override {}
+ void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) override {}
+ void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) override {}
+
+ void OnEncryptionStateChanged(EncryptionChangeView encryption_change_view) override {
if (!encryption_change_view.IsValid()) {
LOG_ERROR("Received on_encryption_change with invalid packet");
return;
@@ -1190,6 +1193,11 @@
handler_);
}
+ void set_security_module(security::SecurityModule* security_module) {
+ security_manager_ = security_module->GetSecurityManager();
+ security_manager_->RegisterCallbackListener(this, handler_);
+ }
+
void accept_connection(Address address) {
auto role = AcceptConnectionRequestRole::BECOME_MASTER; // We prefer to be master
hci_layer_->EnqueueCommand(AcceptConnectionRequestBuilder::Create(address, role),
@@ -1427,9 +1435,10 @@
}
void handle_le_connection_update(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max,
- uint16_t conn_latency, uint16_t supervision_timeout) {
+ uint16_t conn_latency, uint16_t supervision_timeout, uint16_t min_ce_length,
+ uint16_t max_ce_length) {
auto packet = LeConnectionUpdateBuilder::Create(handle, conn_interval_min, conn_interval_max, conn_latency,
- supervision_timeout, kMinimumCeLength, kMaximumCeLength);
+ supervision_timeout, min_ce_length, max_ce_length);
hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandStatusView status) {
ASSERT(status.IsValid());
ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION);
@@ -1869,8 +1878,9 @@
}
bool LeConnectionUpdate(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max,
- uint16_t conn_latency, uint16_t supervision_timeout,
- common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler) {
+ uint16_t conn_latency, uint16_t supervision_timeout, uint16_t min_ce_length,
+ uint16_t max_ce_length, common::OnceCallback<void(ErrorCode)> done_callback,
+ os::Handler* handler) {
auto& connection = check_and_get_connection(handle);
if (connection.is_disconnected_) {
LOG_INFO("Already disconnected");
@@ -1889,7 +1899,7 @@
return false;
}
handler_->Post(BindOnce(&impl::handle_le_connection_update, common::Unretained(this), handle, conn_interval_min,
- conn_interval_max, conn_latency, supervision_timeout));
+ conn_interval_max, conn_latency, supervision_timeout, min_ce_length, max_ce_length));
return true;
}
@@ -1913,6 +1923,7 @@
std::map<uint16_t, acl_connection>::iterator current_connection_pair_;
HciLayer* hci_layer_ = nullptr;
+ std::unique_ptr<security::SecurityManager> security_manager_;
os::Handler* handler_ = nullptr;
ConnectionCallbacks* client_callbacks_ = nullptr;
os::Handler* client_handler_ = nullptr;
@@ -2074,10 +2085,11 @@
}
bool AclConnection::LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
- uint16_t supervision_timeout,
+ uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length,
common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler) {
return manager_->pimpl_->LeConnectionUpdate(handle_, conn_interval_min, conn_interval_max, conn_latency,
- supervision_timeout, std::move(done_callback), handler);
+ supervision_timeout, min_ce_length, max_ce_length,
+ std::move(done_callback), handler);
}
void AclConnection::Finish() {
@@ -2140,6 +2152,10 @@
default_link_policy_settings));
}
+void AclManager::SetSecurityModule(security::SecurityModule* security_module) {
+ GetHandler()->Post(BindOnce(&impl::set_security_module, common::Unretained(pimpl_.get()), security_module));
+}
+
void AclManager::ListDependencies(ModuleList* list) {
list->add<HciLayer>();
list->add<Controller>();
diff --git a/gd/hci/acl_manager.h b/gd/hci/acl_manager.h
index 9d24835..38be104 100644
--- a/gd/hci/acl_manager.h
+++ b/gd/hci/acl_manager.h
@@ -28,6 +28,11 @@
#include "os/handler.h"
namespace bluetooth {
+
+namespace security {
+class SecurityModule;
+}
+
namespace hci {
class AclManager;
@@ -149,8 +154,8 @@
// LE ACL Method
virtual bool LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
- uint16_t supervision_timeout, common::OnceCallback<void(ErrorCode)> done_callback,
- os::Handler* handler);
+ uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length,
+ common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler);
// Ask AclManager to clean me up. Must invoke after on_disconnect is called
virtual void Finish();
@@ -239,6 +244,9 @@
virtual void ReadDefaultLinkPolicySettings();
virtual void WriteDefaultLinkPolicySettings(uint16_t default_link_policy_settings);
+ // In order to avoid circular dependency use setter rather than module dependency.
+ virtual void SetSecurityModule(security::SecurityModule* security_module);
+
static const ModuleFactory Factory;
protected:
diff --git a/gd/hci/acl_manager_test.cc b/gd/hci/acl_manager_test.cc
index 13d156f..4fb71a7 100644
--- a/gd/hci/acl_manager_test.cc
+++ b/gd/hci/acl_manager_test.cc
@@ -588,7 +588,7 @@
std::promise<ErrorCode> promise;
auto future = promise.get_future();
connection->LeConnectionUpdate(
- 0x0006, 0x0C80, 0x0000, 0x000A,
+ 0x0006, 0x0C80, 0x0000, 0x000A, 0, 0,
common::BindOnce([](std::promise<ErrorCode> promise, ErrorCode code) { promise.set_value(code); },
std::move(promise)),
client_handler_);
diff --git a/gd/hci/cert/acl_manager_test.py b/gd/hci/cert/acl_manager_test.py
index abc2dfb..143f701 100644
--- a/gd/hci/cert/acl_manager_test.py
+++ b/gd/hci/cert/acl_manager_test.py
@@ -18,7 +18,7 @@
import sys
import logging
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from cert.truth import assertThat
from google.protobuf import empty_pb2 as empty_proto
@@ -35,12 +35,12 @@
from cert.py_acl_manager import PyAclManager
-class AclManagerTest(GdFacadeOnlyBaseTestClass):
+class AclManagerTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
- # todo: move into GdFacadeOnlyBaseTestClass, based on modules inited
+ # todo: move into GdBaseTestClass, based on modules inited
def setup_test(self):
super().setup_test()
self.cert_hci = PyHci(self.cert)
diff --git a/gd/hci/cert/controller_test.py b/gd/hci/cert/controller_test.py
index 7f6ef2e..6e6030f 100644
--- a/gd/hci/cert/controller_test.py
+++ b/gd/hci/cert/controller_test.py
@@ -16,14 +16,14 @@
import time
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.truth import assertThat
from google.protobuf import empty_pb2 as empty_proto
from facade import rootservice_pb2 as facade_rootservice
from hci.facade import controller_facade_pb2 as controller_facade
-class ControllerTest(GdFacadeOnlyBaseTestClass):
+class ControllerTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(
diff --git a/gd/hci/cert/direct_hci_test.py b/gd/hci/cert/direct_hci_test.py
index 836c076..8c15456 100644
--- a/gd/hci/cert/direct_hci_test.py
+++ b/gd/hci/cert/direct_hci_test.py
@@ -19,7 +19,7 @@
import sys
import logging
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from google.protobuf import empty_pb2 as empty_proto
from facade import rootservice_pb2 as facade_rootservice
@@ -29,7 +29,7 @@
import bluetooth_packets_python3 as bt_packets
-class DirectHciTest(GdFacadeOnlyBaseTestClass):
+class DirectHciTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='HCI', cert_module='HAL')
diff --git a/gd/hci/cert/le_acl_manager_test.py b/gd/hci/cert/le_acl_manager_test.py
index 7de7e99..a90a3de 100644
--- a/gd/hci/cert/le_acl_manager_test.py
+++ b/gd/hci/cert/le_acl_manager_test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from google.protobuf import empty_pb2 as empty_proto
from facade import rootservice_pb2 as facade_rootservice
@@ -26,7 +26,7 @@
from bluetooth_packets_python3 import hci_packets
-class LeAclManagerTest(GdFacadeOnlyBaseTestClass):
+class LeAclManagerTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
diff --git a/gd/hci/cert/le_advertising_manager_test.py b/gd/hci/cert/le_advertising_manager_test.py
index 6a35099..722fb10 100644
--- a/gd/hci/cert/le_advertising_manager_test.py
+++ b/gd/hci/cert/le_advertising_manager_test.py
@@ -18,7 +18,7 @@
import sys
import logging
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from google.protobuf import empty_pb2 as empty_proto
from facade import rootservice_pb2 as facade_rootservice
@@ -29,7 +29,7 @@
from facade import common_pb2 as common
-class LeAdvertisingManagerTest(GdFacadeOnlyBaseTestClass):
+class LeAdvertisingManagerTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
diff --git a/gd/hci/cert/le_scanning_manager_test.py b/gd/hci/cert/le_scanning_manager_test.py
index 95319a1..41e4790 100644
--- a/gd/hci/cert/le_scanning_manager_test.py
+++ b/gd/hci/cert/le_scanning_manager_test.py
@@ -18,7 +18,7 @@
import sys
import logging
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from google.protobuf import empty_pb2 as empty_proto
from facade import rootservice_pb2 as facade_rootservice
@@ -29,7 +29,7 @@
from facade import common_pb2 as common
-class LeScanningManagerTest(GdFacadeOnlyBaseTestClass):
+class LeScanningManagerTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(
diff --git a/gd/hci/hci_layer.cc b/gd/hci/hci_layer.cc
index 462ae01..167cc20 100644
--- a/gd/hci/hci_layer.cc
+++ b/gd/hci/hci_layer.cc
@@ -97,19 +97,52 @@
}
} // namespace
-class SecurityInterfaceImpl : public SecurityInterface {
+class AclConnectionManagerInterfaceImpl : public AclConnectionInterface {
public:
- SecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {}
- virtual ~SecurityInterfaceImpl() = default;
+ explicit AclConnectionManagerInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+ ~AclConnectionManagerInterfaceImpl() override = default;
- virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
- common::OnceCallback<void(CommandCompleteView)> on_complete,
- os::Handler* handler) override {
+ void EnqueueCommand(std::unique_ptr<ConnectionManagementCommandBuilder> command,
+ common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
}
- virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
- common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+ void EnqueueCommand(std::unique_ptr<ConnectionManagementCommandBuilder> command,
+ common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+ hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
+ }
+ HciLayer& hci_;
+};
+
+class SecurityInterfaceImpl : public SecurityInterface {
+ public:
+ explicit SecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+ ~SecurityInterfaceImpl() override = default;
+
+ void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
+ common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+ hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
+ }
+
+ void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
+ common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+ hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
+ }
+ HciLayer& hci_;
+};
+
+class LeAclConnectionManagerInterfaceImpl : public LeAclConnectionInterface {
+ public:
+ explicit LeAclConnectionManagerInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+ ~LeAclConnectionManagerInterfaceImpl() override = default;
+
+ void EnqueueCommand(std::unique_ptr<LeConnectionManagementCommandBuilder> command,
+ common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+ hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
+ }
+
+ void EnqueueCommand(std::unique_ptr<LeConnectionManagementCommandBuilder> command,
+ common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
}
HciLayer& hci_;
@@ -117,17 +150,16 @@
class LeSecurityInterfaceImpl : public LeSecurityInterface {
public:
- LeSecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {}
- virtual ~LeSecurityInterfaceImpl() = default;
+ explicit LeSecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+ ~LeSecurityInterfaceImpl() override = default;
- virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
- common::OnceCallback<void(CommandCompleteView)> on_complete,
- os::Handler* handler) override {
+ void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
+ common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
}
- virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
- common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+ void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
+ common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
}
HciLayer& hci_;
@@ -135,17 +167,16 @@
class LeAdvertisingInterfaceImpl : public LeAdvertisingInterface {
public:
- LeAdvertisingInterfaceImpl(HciLayer& hci) : hci_(hci) {}
- virtual ~LeAdvertisingInterfaceImpl() = default;
+ explicit LeAdvertisingInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+ ~LeAdvertisingInterfaceImpl() override = default;
- virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
- common::OnceCallback<void(CommandCompleteView)> on_complete,
- os::Handler* handler) override {
+ void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
+ common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
}
- virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
- common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+ void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
+ common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
}
HciLayer& hci_;
@@ -153,17 +184,16 @@
class LeScanningInterfaceImpl : public LeScanningInterface {
public:
- LeScanningInterfaceImpl(HciLayer& hci) : hci_(hci) {}
- virtual ~LeScanningInterfaceImpl() = default;
+ explicit LeScanningInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+ ~LeScanningInterfaceImpl() override = default;
- virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
- common::OnceCallback<void(CommandCompleteView)> on_complete,
- os::Handler* handler) override {
+ void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
+ common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
}
- virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
- common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+ void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
+ common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
}
HciLayer& hci_;
@@ -382,8 +412,7 @@
os::Handler* handler) {
ASSERT_LOG(event_handlers_.count(event_code) == 0, "Can not register a second handler for event_code %02hhx (%s)",
event_code, EventCodeText(event_code).c_str());
- EventHandler to_save(event_handler, handler);
- event_handlers_[event_code] = to_save;
+ event_handlers_[event_code] = EventHandler(event_handler, handler);
}
void UnregisterEventHandler(EventCode event_code) {
@@ -406,8 +435,7 @@
ASSERT_LOG(subevent_handlers_.count(subevent_code) == 0,
"Can not register a second handler for subevent_code %02hhx (%s)", subevent_code,
SubeventCodeText(subevent_code).c_str());
- SubeventHandler to_save(subevent_handler, handler);
- subevent_handlers_[subevent_code] = to_save;
+ subevent_handlers_[subevent_code] = SubeventHandler(subevent_handler, handler);
}
void UnregisterLeEventHandler(SubeventCode subevent_code) {
@@ -426,6 +454,8 @@
HciLayer& module_;
// Interfaces
+ AclConnectionManagerInterfaceImpl acl_connection_manager_interface_{module_};
+ LeAclConnectionManagerInterfaceImpl le_acl_connection_manager_interface_{module_};
SecurityInterfaceImpl security_interface{module_};
LeSecurityInterfaceImpl le_security_interface{module_};
LeAdvertisingInterfaceImpl le_advertising_interface{module_};
@@ -483,6 +513,24 @@
impl_->UnregisterLeEventHandler(subevent_code);
}
+AclConnectionInterface* HciLayer::GetAclConnectionInterface(common::Callback<void(EventPacketView)> event_handler,
+ common::Callback<void(uint16_t, ErrorCode)> on_disconnect,
+ os::Handler* handler) {
+ for (const auto event : AclConnectionInterface::AclConnectionEvents) {
+ RegisterEventHandler(event, event_handler, handler);
+ }
+ return &impl_->acl_connection_manager_interface_;
+}
+
+LeAclConnectionInterface* HciLayer::GetLeAclConnectionInterface(
+ common::Callback<void(LeMetaEventView)> event_handler, common::Callback<void(uint16_t, ErrorCode)> on_disconnect,
+ os::Handler* handler) {
+ for (const auto event : LeAclConnectionInterface::LeConnectionManagementEvents) {
+ RegisterLeEventHandler(event, event_handler, handler);
+ }
+ return &impl_->le_acl_connection_manager_interface_;
+}
+
SecurityInterface* HciLayer::GetSecurityInterface(common::Callback<void(EventPacketView)> event_handler,
os::Handler* handler) {
for (const auto event : SecurityInterface::SecurityEvents) {
diff --git a/gd/hci/hci_layer.h b/gd/hci/hci_layer.h
index 3e299d4..50686cd 100644
--- a/gd/hci/hci_layer.h
+++ b/gd/hci/hci_layer.h
@@ -24,7 +24,9 @@
#include "common/bidi_queue.h"
#include "common/callback.h"
#include "hal/hci_hal.h"
+#include "hci/acl_connection_interface.h"
#include "hci/hci_packets.h"
+#include "hci/le_acl_connection_interface.h"
#include "hci/le_advertising_interface.h"
#include "hci/le_scanning_interface.h"
#include "hci/le_security_interface.h"
@@ -64,6 +66,14 @@
LeSecurityInterface* GetLeSecurityInterface(common::Callback<void(LeMetaEventView)> event_handler,
os::Handler* handler);
+ AclConnectionInterface* GetAclConnectionInterface(common::Callback<void(EventPacketView)> event_handler,
+ common::Callback<void(uint16_t, hci::ErrorCode)> on_disconnect,
+ os::Handler* handler);
+
+ LeAclConnectionInterface* GetLeAclConnectionInterface(common::Callback<void(LeMetaEventView)> event_handler,
+ common::Callback<void(uint16_t, hci::ErrorCode)> on_disconnect,
+ os::Handler* handler);
+
LeAdvertisingInterface* GetLeAdvertisingInterface(common::Callback<void(LeMetaEventView)> event_handler,
os::Handler* handler);
diff --git a/gd/hci/hci_packets.pdl b/gd/hci/hci_packets.pdl
index 3658c98..f37383a 100644
--- a/gd/hci/hci_packets.pdl
+++ b/gd/hci/hci_packets.pdl
@@ -1034,7 +1034,7 @@
bd_addr : Address,
}
-packet ReadRemoteSupportedFeatures : DiscoveryCommand (op_code = READ_REMOTE_SUPPORTED_FEATURES) {
+packet ReadRemoteSupportedFeatures : ConnectionManagementCommand (op_code = READ_REMOTE_SUPPORTED_FEATURES) {
connection_handle : 12,
_reserved_ : 4,
}
@@ -1042,7 +1042,7 @@
packet ReadRemoteSupportedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_SUPPORTED_FEATURES) {
}
-packet ReadRemoteExtendedFeatures : DiscoveryCommand (op_code = READ_REMOTE_EXTENDED_FEATURES) {
+packet ReadRemoteExtendedFeatures : ConnectionManagementCommand (op_code = READ_REMOTE_EXTENDED_FEATURES) {
connection_handle : 12,
_reserved_ : 4,
page_number : 8,
@@ -1051,7 +1051,7 @@
packet ReadRemoteExtendedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_EXTENDED_FEATURES) {
}
-packet ReadRemoteVersionInformation : DiscoveryCommand (op_code = READ_REMOTE_VERSION_INFORMATION) {
+packet ReadRemoteVersionInformation : ConnectionManagementCommand (op_code = READ_REMOTE_VERSION_INFORMATION) {
connection_handle : 12,
_reserved_ : 4,
}
diff --git a/gd/hci/le_acl_connection_interface.h b/gd/hci/le_acl_connection_interface.h
new file mode 100644
index 0000000..627461e
--- /dev/null
+++ b/gd/hci/le_acl_connection_interface.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+class LeAclConnectionInterface {
+ public:
+ LeAclConnectionInterface() = default;
+ virtual ~LeAclConnectionInterface() = default;
+ DISALLOW_COPY_AND_ASSIGN(LeAclConnectionInterface);
+
+ virtual void EnqueueCommand(std::unique_ptr<LeConnectionManagementCommandBuilder> command,
+ common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
+
+ virtual void EnqueueCommand(std::unique_ptr<LeConnectionManagementCommandBuilder> command,
+ common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
+
+ static constexpr SubeventCode LeConnectionManagementEvents[] = {
+ SubeventCode::CONNECTION_COMPLETE,
+ SubeventCode::ENHANCED_CONNECTION_COMPLETE,
+ SubeventCode::CONNECTION_UPDATE_COMPLETE,
+ };
+};
+} // namespace hci
+} // namespace bluetooth
diff --git a/gd/hci/security_interface.h b/gd/hci/security_interface.h
index 1258122..0d945b0 100644
--- a/gd/hci/security_interface.h
+++ b/gd/hci/security_interface.h
@@ -36,6 +36,7 @@
common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
static constexpr hci::EventCode SecurityEvents[] = {
+ hci::EventCode::ENCRYPTION_CHANGE,
hci::EventCode::CHANGE_CONNECTION_LINK_KEY_COMPLETE,
hci::EventCode::MASTER_LINK_KEY_COMPLETE,
hci::EventCode::RETURN_LINK_KEYS,
diff --git a/gd/l2cap/Android.bp b/gd/l2cap/Android.bp
index c04a9d3..ff68c10 100644
--- a/gd/l2cap/Android.bp
+++ b/gd/l2cap/Android.bp
@@ -70,6 +70,7 @@
name: "BluetoothFacade_l2cap_layer",
srcs: [
"classic/facade.cc",
+ "le/facade.cc",
],
}
diff --git a/gd/l2cap/classic/cert/l2cap_test.py b/gd/l2cap/classic/cert/l2cap_test.py
index 30b8466..06f5296 100644
--- a/gd/l2cap/classic/cert/l2cap_test.py
+++ b/gd/l2cap/classic/cert/l2cap_test.py
@@ -17,7 +17,7 @@
from datetime import timedelta
from mobly import asserts
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from cert.truth import assertThat
from cert.closable import safeClose
@@ -44,7 +44,7 @@
SAMPLE_PACKET = l2cap_packets.CommandRejectNotUnderstoodBuilder(1)
-class L2capTest(GdFacadeOnlyBaseTestClass):
+class L2capTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='L2CAP', cert_module='HCI_INTERFACES')
@@ -63,6 +63,7 @@
def teardown_test(self):
self.cert_l2cap.close()
+ self.dut_l2cap.close()
super().teardown_test()
def cert_send_b_frame(self, b_frame):
@@ -105,17 +106,6 @@
dut_channel.send(b'abc')
assertThat(cert_channel).emits(L2capMatchers.Data(b'abc'))
- def test_fixed_channel(self):
- self._setup_link_from_cert()
-
- self.dut.l2cap.RegisterChannel(
- l2cap_facade_pb2.RegisterChannelRequest(channel=2))
- asserts.skip("FIXME: Not working")
- self.dut.l2cap.SendL2capPacket(
- l2cap_facade_pb2.L2capPacket(channel=2, payload=b"123"))
-
- assertThat(self.cert_channel).emits(L2capMatchers.PartialData(b'123'))
-
def test_receive_packet_from_unknown_channel(self):
self._setup_link_from_cert()
@@ -610,6 +600,57 @@
L2capMatchers.IFrame(tx_seq=0, payload=b'abc'),
L2capMatchers.IFrame(tx_seq=1, payload=b'abc')).inOrder()
+ def test_respond_to_srej_p_set(self):
+ """
+ L2CAP/ERM/BV-14-C [Respond to S-Frame [SREJ] POLL Bit Set]
+ """
+ self._setup_link_from_cert()
+ self.cert_l2cap.turn_on_ertm(tx_window_size=3, max_transmit=2)
+
+ (dut_channel, cert_channel) = self._open_channel(
+ scid=0x41, psm=0x33, use_ertm=True)
+
+ for _ in range(4):
+ dut_channel.send(b'abc')
+ assertThat(cert_channel).emits(
+ L2capMatchers.IFrame(tx_seq=0, payload=b'abc'),
+ L2capMatchers.IFrame(tx_seq=1, payload=b'abc'),
+ L2capMatchers.IFrame(tx_seq=2, payload=b'abc')).inOrder()
+
+ cert_channel.send_s_frame(
+ req_seq=1, p=Poll.POLL, s=SupervisoryFunction.SELECT_REJECT)
+
+ assertThat(cert_channel).emits(
+ L2capMatchers.IFrame(
+ tx_seq=1, payload=b'abc', f=Final.POLL_RESPONSE),
+ L2capMatchers.IFrame(tx_seq=3, payload=b'abc')).inOrder()
+
+ def test_respond_to_srej_p_clear(self):
+ """
+ L2CAP/ERM/BV-15-C [Respond to S-Frame [SREJ] POLL Bit Clear]
+ """
+ self._setup_link_from_cert()
+ self.cert_l2cap.turn_on_ertm(tx_window_size=3, max_transmit=2)
+
+ (dut_channel, cert_channel) = self._open_channel(
+ scid=0x41, psm=0x33, use_ertm=True)
+
+ for _ in range(4):
+ dut_channel.send(b'abc')
+ assertThat(cert_channel).emits(
+ L2capMatchers.IFrame(tx_seq=0, payload=b'abc'),
+ L2capMatchers.IFrame(tx_seq=1, payload=b'abc'),
+ L2capMatchers.IFrame(tx_seq=2, payload=b'abc')).inOrder()
+
+ cert_channel.send_s_frame(
+ req_seq=1, s=SupervisoryFunction.SELECT_REJECT)
+ assertThat(cert_channel).emits(
+ L2capMatchers.IFrame(tx_seq=1, payload=b'abc', f=Final.NOT_SET))
+ cert_channel.send_s_frame(
+ req_seq=3, s=SupervisoryFunction.RECEIVER_READY)
+ assertThat(cert_channel).emits(
+ L2capMatchers.IFrame(tx_seq=3, payload=b'abc', f=Final.NOT_SET))
+
def test_receive_s_frame_rr_final_bit_set(self):
"""
L2CAP/ERM/BV-18-C [Receive S-Frame [RR] Final Bit = 1]
diff --git a/gd/l2cap/classic/facade.cc b/gd/l2cap/classic/facade.cc
index 64e0068..35a518f 100644
--- a/gd/l2cap/classic/facade.cc
+++ b/gd/l2cap/classic/facade.cc
@@ -25,7 +25,6 @@
#include "l2cap/classic/facade.grpc.pb.h"
#include "l2cap/classic/facade.h"
#include "l2cap/classic/l2cap_classic_module.h"
-#include "l2cap/l2cap_packets.h"
#include "os/log.h"
#include "packet/raw_builder.h"
@@ -57,20 +56,6 @@
return pending_connection_close_.RunLoop(context, writer);
}
- ::grpc::Status SendL2capPacket(::grpc::ServerContext* context, const classic::L2capPacket* request,
- SendL2capPacketResult* response) override {
- std::unique_lock<std::mutex> lock(channel_map_mutex_);
- if (fixed_channel_helper_map_.find(request->channel()) == fixed_channel_helper_map_.end()) {
- return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not registered");
- }
- std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
- if (!fixed_channel_helper_map_[request->channel()]->SendPacket(packet)) {
- return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
- }
- response->set_result_type(SendL2capPacketResultType::OK);
- return ::grpc::Status::OK;
- }
-
::grpc::Status SendDynamicChannelPacket(::grpc::ServerContext* context, const DynamicChannelPacket* request,
::google::protobuf::Empty* response) override {
std::unique_lock<std::mutex> lock(channel_map_mutex_);
@@ -116,103 +101,6 @@
return status;
}
- ::grpc::Status RegisterChannel(::grpc::ServerContext* context, const classic::RegisterChannelRequest* request,
- ::google::protobuf::Empty* response) override {
- std::unique_lock<std::mutex> lock(channel_map_mutex_);
- if (fixed_channel_helper_map_.find(request->channel()) != fixed_channel_helper_map_.end()) {
- return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Already registered");
- }
- fixed_channel_helper_map_.emplace(request->channel(), std::make_unique<L2capFixedChannelHelper>(
- this, l2cap_layer_, facade_handler_, request->channel()));
-
- return ::grpc::Status::OK;
- }
-
- class L2capFixedChannelHelper {
- public:
- L2capFixedChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
- os::Handler* handler, Cid cid)
- : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), cid_(cid) {
- fixed_channel_manager_ = l2cap_layer_->GetFixedChannelManager();
- fixed_channel_manager_->RegisterService(
- cid, {},
- common::BindOnce(&L2capFixedChannelHelper::on_l2cap_service_registration_complete, common::Unretained(this)),
- common::Bind(&L2capFixedChannelHelper::on_connection_open, common::Unretained(this)), handler_);
- }
-
- void on_l2cap_service_registration_complete(FixedChannelManager::RegistrationResult registration_result,
- std::unique_ptr<FixedChannelService> service) {
- service_ = std::move(service);
- }
-
- void on_connection_open(std::unique_ptr<FixedChannel> channel) {
- ConnectionCompleteEvent event;
- event.mutable_remote()->set_address(channel->GetDevice().ToString());
- facade_service_->pending_connection_complete_.OnIncomingEvent(event);
- channel_ = std::move(channel);
- channel_->RegisterOnCloseCallback(
- facade_service_->facade_handler_,
- common::BindOnce(&L2capFixedChannelHelper::on_close_callback, common::Unretained(this)));
- {
- std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
- if (facade_service_->fetch_l2cap_data_) {
- channel_->GetQueueUpEnd()->RegisterDequeue(
- facade_service_->facade_handler_,
- common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(this)));
- }
- }
- }
-
- bool SendPacket(const std::vector<uint8_t>& packet) {
- if (channel_ == nullptr) {
- LOG_WARN("Channel is not open");
- return false;
- }
- std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
- channel_->GetQueueUpEnd()->RegisterEnqueue(
- handler_, common::Bind(&L2capFixedChannelHelper::enqueue_callback, common::Unretained(this), packet));
- return true;
- }
-
- void on_close_callback(hci::ErrorCode error_code) {
- {
- std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
- if (facade_service_->fetch_l2cap_data_) {
- channel_->GetQueueUpEnd()->UnregisterDequeue();
- }
- }
- channel_ = nullptr;
- classic::ConnectionCloseEvent event;
- event.mutable_remote()->set_address(channel_->GetDevice().ToString());
- event.set_reason(static_cast<uint32_t>(error_code));
- facade_service_->pending_connection_close_.OnIncomingEvent(event);
- }
-
- void on_incoming_packet() {
- auto packet = channel_->GetQueueUpEnd()->TryDequeue();
- std::string data = std::string(packet->begin(), packet->end());
- L2capPacket l2cap_data;
- l2cap_data.set_channel(cid_);
- l2cap_data.set_payload(data);
- facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
- }
-
- std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(const std::vector<uint8_t>& packet) {
- auto packet_one = std::make_unique<packet::RawBuilder>();
- packet_one->AddOctets(packet);
- channel_->GetQueueUpEnd()->UnregisterEnqueue();
- return packet_one;
- };
-
- L2capClassicModuleFacadeService* facade_service_;
- L2capClassicModule* l2cap_layer_;
- os::Handler* handler_;
- std::unique_ptr<FixedChannelManager> fixed_channel_manager_;
- std::unique_ptr<FixedChannelService> service_;
- std::unique_ptr<FixedChannel> channel_ = nullptr;
- Cid cid_;
- };
-
::grpc::Status SetDynamicChannel(::grpc::ServerContext* context, const SetEnableDynamicChannelRequest* request,
google::protobuf::Empty* response) override {
dynamic_channel_helper_map_.emplace(
@@ -266,7 +154,7 @@
// invoked from Facade Handler
void on_connection_open(std::unique_ptr<DynamicChannel> channel) {
ConnectionCompleteEvent event;
- event.mutable_remote()->set_address(channel->GetDevice().ToString());
+ event.mutable_remote()->set_address(channel->GetDevice().GetAddress().ToString());
facade_service_->pending_connection_complete_.OnIncomingEvent(event);
{
std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
@@ -287,7 +175,7 @@
channel_->GetQueueUpEnd()->UnregisterDequeue();
}
classic::ConnectionCloseEvent event;
- event.mutable_remote()->set_address(channel_->GetDevice().ToString());
+ event.mutable_remote()->set_address(channel_->GetDevice().GetAddress().ToString());
event.set_reason(static_cast<uint32_t>(error_code));
facade_service_->pending_connection_close_.OnIncomingEvent(event);
channel_ = nullptr;
@@ -348,7 +236,6 @@
L2capClassicModule* l2cap_layer_;
::bluetooth::os::Handler* facade_handler_;
std::mutex channel_map_mutex_;
- std::map<Cid, std::unique_ptr<L2capFixedChannelHelper>> fixed_channel_helper_map_;
std::map<Psm, std::unique_ptr<L2capDynamicChannelHelper>> dynamic_channel_helper_map_;
bool fetch_l2cap_data_ = false;
::bluetooth::grpc::GrpcEventQueue<classic::ConnectionCompleteEvent> pending_connection_complete_{
diff --git a/gd/l2cap/classic/facade.proto b/gd/l2cap/classic/facade.proto
index 758f568..851d5ea 100644
--- a/gd/l2cap/classic/facade.proto
+++ b/gd/l2cap/classic/facade.proto
@@ -6,9 +6,6 @@
import "facade/common.proto";
service L2capClassicModuleFacade {
- rpc RegisterChannel(RegisterChannelRequest) returns (google.protobuf.Empty) {
- // Testing Android Bluetooth stack only. Optional for other stack.
- }
rpc FetchConnectionComplete(google.protobuf.Empty) returns (stream ConnectionCompleteEvent) {
// Testing Android Bluetooth stack only. Optional for other stack.
}
@@ -17,7 +14,6 @@
}
rpc OpenChannel(OpenChannelRequest) returns (google.protobuf.Empty) {}
rpc CloseChannel(CloseChannelRequest) returns (google.protobuf.Empty) {}
- rpc SendL2capPacket(L2capPacket) returns (SendL2capPacketResult) {}
rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {}
rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {}
rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {}
diff --git a/gd/l2cap/classic/internal/signalling_manager.cc b/gd/l2cap/classic/internal/signalling_manager.cc
index 8012db3..9193270 100644
--- a/gd/l2cap/classic/internal/signalling_manager.cc
+++ b/gd/l2cap/classic/internal/signalling_manager.cc
@@ -58,8 +58,7 @@
}
void ClassicSignallingManager::OnCommandReject(CommandRejectView command_reject_view) {
- if (command_just_sent_.signal_id_ != command_reject_view.GetIdentifier() ||
- command_just_sent_.command_code_ != command_reject_view.GetCode()) {
+ if (command_just_sent_.signal_id_ != command_reject_view.GetIdentifier()) {
LOG_WARN("Unexpected command reject: no pending request");
return;
}
diff --git a/gd/l2cap/dynamic_channel.cc b/gd/l2cap/dynamic_channel.cc
index f8b64e6..da599a9 100644
--- a/gd/l2cap/dynamic_channel.cc
+++ b/gd/l2cap/dynamic_channel.cc
@@ -21,7 +21,7 @@
namespace bluetooth {
namespace l2cap {
-hci::Address DynamicChannel::GetDevice() const {
+hci::AddressWithType DynamicChannel::GetDevice() const {
return impl_->GetDevice();
}
diff --git a/gd/l2cap/dynamic_channel.h b/gd/l2cap/dynamic_channel.h
index b7496f8..745fa37 100644
--- a/gd/l2cap/dynamic_channel.h
+++ b/gd/l2cap/dynamic_channel.h
@@ -42,7 +42,7 @@
ASSERT(l2cap_handler_ != nullptr);
}
- hci::Address GetDevice() const;
+ hci::AddressWithType GetDevice() const;
/**
* Register close callback. If close callback is registered, when a channel is closed, the channel's resource will
diff --git a/gd/l2cap/internal/dynamic_channel_impl.cc b/gd/l2cap/internal/dynamic_channel_impl.cc
index 947a32f..3a0918a 100644
--- a/gd/l2cap/internal/dynamic_channel_impl.cc
+++ b/gd/l2cap/internal/dynamic_channel_impl.cc
@@ -40,8 +40,8 @@
ASSERT(l2cap_handler_ != nullptr);
}
-hci::Address DynamicChannelImpl::GetDevice() const {
- return device_.GetAddress();
+hci::AddressWithType DynamicChannelImpl::GetDevice() const {
+ return device_;
}
void DynamicChannelImpl::RegisterOnCloseCallback(os::Handler* user_handler,
diff --git a/gd/l2cap/internal/dynamic_channel_impl.h b/gd/l2cap/internal/dynamic_channel_impl.h
index 856e21d..c980012 100644
--- a/gd/l2cap/internal/dynamic_channel_impl.h
+++ b/gd/l2cap/internal/dynamic_channel_impl.h
@@ -38,7 +38,7 @@
virtual ~DynamicChannelImpl() = default;
- hci::Address GetDevice() const;
+ hci::AddressWithType GetDevice() const;
virtual void RegisterOnCloseCallback(os::Handler* user_handler, DynamicChannel::OnCloseCallback on_close_callback);
diff --git a/gd/l2cap/internal/dynamic_channel_impl_test.cc b/gd/l2cap/internal/dynamic_channel_impl_test.cc
index 677b504..ee38430 100644
--- a/gd/l2cap/internal/dynamic_channel_impl_test.cc
+++ b/gd/l2cap/internal/dynamic_channel_impl_test.cc
@@ -65,7 +65,7 @@
EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
DynamicChannelImpl dynamic_channel_impl(0x01, kFirstDynamicChannel, kFirstDynamicChannel, &mock_classic_link,
l2cap_handler_);
- EXPECT_EQ(device.GetAddress(), dynamic_channel_impl.GetDevice());
+ EXPECT_EQ(device, dynamic_channel_impl.GetDevice());
}
TEST_F(L2capClassicDynamicChannelImplTest, close_triggers_callback) {
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
index dc98836..ca1160a 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -411,6 +411,7 @@
remote_busy_ = false;
pass_to_tx(req_seq, f);
retransmit_requested_i_frame(req_seq, p);
+ send_pending_i_frames();
if (p_bit_outstanding()) {
srej_actioned_ = true;
srej_save_req_seq_ = req_seq;
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
index 943cb17..6769763 100644
--- a/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
@@ -102,6 +102,7 @@
}
std::unique_ptr<packet::BasePacketBuilder> LeCreditBasedDataController::GetNextPacket() {
+ ASSERT(!pdu_queue_.empty());
auto next = std::move(pdu_queue_.front());
pdu_queue_.pop();
return next;
@@ -123,6 +124,7 @@
credits_ = total_credits;
if (pending_frames_count_ > 0 && credits_ >= pending_frames_count_) {
scheduler_->OnPacketsReady(cid_, pending_frames_count_);
+ pending_frames_count_ = 0;
credits_ -= pending_frames_count_;
} else if (pending_frames_count_ > 0) {
scheduler_->OnPacketsReady(cid_, credits_);
diff --git a/gd/l2cap/l2cap_packet_test.cc b/gd/l2cap/l2cap_packet_test.cc
index 33752e0..7a413f4 100644
--- a/gd/l2cap/l2cap_packet_test.cc
+++ b/gd/l2cap/l2cap_packet_test.cc
@@ -141,5 +141,15 @@
RunConfigurationRequestReflectionFuzzTest(bluetooth_gd_fuzz_test_5747922062802944,
sizeof(bluetooth_gd_fuzz_test_5747922062802944));
}
+
+TEST(L2capFuzzRegressions, ConfigurationRequestFuzz_5202709231697920) {
+ uint8_t bluetooth_gd_fuzz_test_5747922062802944[] = {
+ 0x04, 0x01, 0x45, 0x45, 0x05, 0x01, 0x01, 0x45, 0x05, 0x01,
+ };
+
+ RunConfigurationRequestReflectionFuzzTest(bluetooth_gd_fuzz_test_5747922062802944,
+ sizeof(bluetooth_gd_fuzz_test_5747922062802944));
+}
+
} // namespace l2cap
} // namespace bluetooth
diff --git a/gd/l2cap/le/cert/cert_le_l2cap.py b/gd/l2cap/le/cert/cert_le_l2cap.py
new file mode 100644
index 0000000..63142c9
--- /dev/null
+++ b/gd/l2cap/le/cert/cert_le_l2cap.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.py_le_acl_manager import PyLeAclManager
+from cert.truth import assertThat
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import l2cap_packets
+from bluetooth_packets_python3.l2cap_packets import LeCommandCode
+from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
+from cert.event_stream import FilteringEventStream
+from cert.event_stream import IEventStream
+from cert.matchers import L2capMatchers
+from cert.captures import L2capCaptures
+
+
+class CertLeL2capChannel(IEventStream):
+
+ def __init__(self, device, scid, dcid, acl_stream, acl, control_channel):
+ self._device = device
+ self._scid = scid
+ self._dcid = dcid
+ self._acl_stream = acl_stream
+ self._acl = acl
+ self._control_channel = control_channel
+ self._our_acl_view = FilteringEventStream(
+ acl_stream, L2capMatchers.ExtractBasicFrame(scid))
+
+ def get_event_queue(self):
+ return self._our_acl_view.get_event_queue()
+
+ def send(self, packet):
+ frame = l2cap_packets.BasicFrameBuilder(self._dcid, packet)
+ self._acl.send(frame.Serialize())
+
+ def send_first_le_i_frame(self, sdu_size, packet):
+ frame = l2cap_packets.FirstLeInformationFrameBuilder(
+ self._dcid, sdu_size, packet)
+ self._acl.send(frame.Serialize())
+
+ def disconnect_and_verify(self):
+ assertThat(self._scid).isNotEqualTo(1)
+ self._control_channel.send(
+ l2cap_packets.DisconnectionRequestBuilder(1, self._dcid,
+ self._scid))
+
+ assertThat(self._control_channel).emits(
+ L2capMatchers.LeDisconnectionResponse(self._scid, self._dcid))
+
+ def verify_disconnect_request(self):
+ assertThat(self._control_channel).emits(
+ L2capMatchers.LeDisconnectionRequest(self._dcid, self._scid))
+
+ def send_credits(self, num_credits):
+ self._control_channel.send(
+ l2cap_packets.LeFlowControlCreditBuilder(2, self._scid,
+ num_credits))
+
+
+class CertLeL2cap(Closable):
+
+ def __init__(self, device):
+ self._device = device
+ self._le_acl_manager = PyLeAclManager(device)
+ self._le_acl = None
+
+ self.control_table = {
+ LeCommandCode.DISCONNECTION_REQUEST:
+ self._on_disconnection_request_default,
+ LeCommandCode.DISCONNECTION_RESPONSE:
+ self._on_disconnection_response_default,
+ }
+
+ self.scid_to_dcid = {}
+
+ def close(self):
+ self._le_acl_manager.close()
+ safeClose(self._le_acl)
+
+ def connect_le_acl(self, remote_addr):
+ self._le_acl = self._le_acl_manager.initiate_connection(remote_addr)
+ self._le_acl.wait_for_connection_complete()
+ self.control_channel = CertLeL2capChannel(
+ self._device,
+ 5,
+ 5,
+ self._get_acl_stream(),
+ self._le_acl,
+ control_channel=None)
+ self._get_acl_stream().register_callback(self._handle_control_packet)
+
+ def open_channel(self,
+ signal_id,
+ psm,
+ scid,
+ mtu=1000,
+ mps=100,
+ initial_credit=6):
+ self.control_channel.send(
+ l2cap_packets.LeCreditBasedConnectionRequestBuilder(
+ signal_id, psm, scid, mtu, mps, initial_credit))
+
+ response = L2capCaptures.CreditBasedConnectionResponse(scid)
+ assertThat(self.control_channel).emits(response)
+ return CertLeL2capChannel(self._device, scid,
+ response.get().GetDestinationCid(),
+ self._get_acl_stream(), self._le_acl,
+ self.control_channel)
+
+ def verify_and_respond_open_channel_from_remote(
+ self, psm=0x33,
+ result=LeCreditBasedConnectionResponseResult.SUCCESS):
+ request = L2capCaptures.CreditBasedConnectionRequest(psm)
+ assertThat(self.control_channel).emits(request)
+ (scid, dcid) = self._respond_connection_request_default(
+ request.get(), result)
+ return CertLeL2capChannel(self._device, scid, dcid,
+ self._get_acl_stream(), self._le_acl,
+ self.control_channel)
+
+ def verify_and_reject_open_channel_from_remote(self, psm=0x33):
+ request = L2capCaptures.CreditBasedConnectionRequest(psm)
+ assertThat(self.control_channel).emits(request)
+ sid = request.get().GetIdentifier()
+ reject = l2cap_packets.LeCommandRejectNotUnderstoodBuilder(sid)
+ self.control_channel.send(reject)
+
+ def _respond_connection_request_default(
+ self, request,
+ result=LeCreditBasedConnectionResponseResult.SUCCESS):
+ sid = request.GetIdentifier()
+ their_scid = request.GetSourceCid()
+ mtu = request.GetMtu()
+ mps = request.GetMps()
+ initial_credits = request.GetInitialCredits()
+ # Here we use the same value - their scid as their scid
+ our_scid = their_scid
+ our_dcid = their_scid
+ response = l2cap_packets.LeCreditBasedConnectionResponseBuilder(
+ sid, our_scid, mtu, mps, initial_credits, result)
+ self.control_channel.send(response)
+ return (our_scid, our_dcid)
+
+ # prefer to use channel abstraction instead, if at all possible
+ def send_acl(self, packet):
+ self._acl.send(packet.Serialize())
+
+ def get_control_channel(self):
+ return self.control_channel
+
+ def _get_acl_stream(self):
+ return self._le_acl_manager.get_le_acl_stream()
+
+ def _on_disconnection_request_default(self, request):
+ disconnection_request = l2cap_packets.LeDisconnectionRequestView(
+ request)
+ sid = disconnection_request.GetIdentifier()
+ scid = disconnection_request.GetSourceCid()
+ dcid = disconnection_request.GetDestinationCid()
+ response = l2cap_packets.LeDisconnectionResponseBuilder(sid, dcid, scid)
+ self.control_channel.send(response)
+
+ def _on_disconnection_response_default(self, request):
+ disconnection_response = l2cap_packets.LeDisconnectionResponseView(
+ request)
+
+ def _handle_control_packet(self, l2cap_packet):
+ packet_bytes = l2cap_packet.payload
+ l2cap_view = l2cap_packets.BasicFrameView(
+ bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+ if l2cap_view.GetChannelId() != 5:
+ return
+ request = l2cap_packets.LeControlView(l2cap_view.GetPayload())
+ fn = self.control_table.get(request.GetCode())
+ if fn is not None:
+ fn(request)
+ return
diff --git a/gd/l2cap/le/cert/le_l2cap_test.py b/gd/l2cap/le/cert/le_l2cap_test.py
new file mode 100644
index 0000000..f742681
--- /dev/null
+++ b/gd/l2cap/le/cert/le_l2cap_test.py
@@ -0,0 +1,315 @@
+#
+# 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.
+
+import time
+from datetime import timedelta
+from mobly import asserts
+
+from cert.gd_base_test import GdBaseTestClass
+from cert.event_stream import EventStream
+from cert.truth import assertThat
+from cert.closable import safeClose
+from cert.py_l2cap import PyLeL2cap
+from cert.py_acl_manager import PyAclManager
+from cert.matchers import L2capMatchers
+from facade import common_pb2 as common
+from facade import rootservice_pb2 as facade_rootservice
+from google.protobuf import empty_pb2 as empty_proto
+from l2cap.le import facade_pb2 as l2cap_facade_pb2
+from neighbor.facade import facade_pb2 as neighbor_facade
+from hci.facade import acl_manager_facade_pb2 as acl_manager_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets, l2cap_packets
+from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
+from l2cap.le.cert.cert_le_l2cap import CertLeL2cap
+
+# Assemble a sample packet. TODO: Use RawBuilder
+SAMPLE_PACKET = l2cap_packets.CommandRejectNotUnderstoodBuilder(1)
+
+
+class LeL2capTest(GdBaseTestClass):
+
+ def setup_class(self):
+ super().setup_class(dut_module='L2CAP', cert_module='HCI_INTERFACES')
+
+ def setup_test(self):
+ super().setup_test()
+
+ self.dut.address = self.dut.hci_controller.GetMacAddressSimple()
+ self.cert.address = self.cert.controller_read_only_property.ReadLocalAddress(
+ empty_proto.Empty()).address
+ self.cert_address = common.BluetoothAddress(address=self.cert.address)
+
+ self.dut_l2cap = PyLeL2cap(self.dut)
+ self.cert_l2cap = CertLeL2cap(self.cert)
+
+ def teardown_test(self):
+ self.cert_l2cap.close()
+ self.dut_l2cap.close()
+ super().teardown_test()
+
+ def _setup_link_from_cert(self):
+ # DUT Advertises
+ gap_name = hci_packets.GapData()
+ gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+ gap_name.data = list(bytes(b'Im_The_DUT'))
+ gap_data = le_advertising_facade.GapDataMsg(
+ data=bytes(gap_name.Serialize()))
+ config = le_advertising_facade.AdvertisingConfig(
+ advertisement=[gap_data],
+ random_address=common.BluetoothAddress(
+ address=bytes(b'0D:05:04:03:02:01')),
+ interval_min=512,
+ interval_max=768,
+ event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+ address_type=common.RANDOM_DEVICE_ADDRESS,
+ peer_address_type=common.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+ peer_address=common.BluetoothAddress(
+ address=bytes(b'A6:A5:A4:A3:A2:A1')),
+ channel_map=7,
+ filter_policy=le_advertising_facade.AdvertisingFilterPolicy.
+ ALL_DEVICES)
+ request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+ create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(
+ request)
+ self.cert_l2cap.connect_le_acl(bytes(b'0D:05:04:03:02:01'))
+
+ def _open_channel_from_cert(self,
+ signal_id=1,
+ scid=0x0101,
+ psm=0x33,
+ mtu=1000,
+ mps=100,
+ initial_credit=6):
+
+ dut_channel = self.dut_l2cap.register_coc(psm)
+ cert_channel = self.cert_l2cap.open_channel(signal_id, psm, scid, mtu,
+ mps, initial_credit)
+
+ return (dut_channel, cert_channel)
+
+ def _open_channel_from_dut(self, psm=0x33):
+ response_future = self.dut_l2cap.connect_coc_to_cert(psm)
+ cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+ psm)
+ dut_channel = response_future.get_channel()
+ return (dut_channel, cert_channel)
+
+ def test_reject_connection_parameter_update_request(self):
+ """
+ L2CAP/LE/CPU/BI-02-C
+ """
+ self._setup_link_from_cert()
+ self.cert_l2cap.get_control_channel().send(
+ l2cap_packets.ConnectionParameterUpdateRequestBuilder(
+ 2, 100, 100, 512, 100))
+ assertThat(self.cert_l2cap.get_control_channel()).emits(
+ L2capMatchers.LeCommandReject())
+
+ def test_segmentation(self):
+ """
+ L2CAP/COS/CFC/BV-01-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel, cert_channel) = self._open_channel_from_cert(
+ mtu=1000, mps=102)
+ dut_channel.send(b'hello' * 20 + b'world')
+ # The first LeInformation packet contains 2 bytes of SDU size.
+ # The packet is divided into first 100 bytes from 'hellohello....'
+ # and remaining 5 bytes 'world'
+ assertThat(cert_channel).emits(
+ L2capMatchers.FirstLeIFrame(b'hello' * 20, sdu_size=105),
+ L2capMatchers.Data(b'world')).inOrder()
+
+ def test_no_segmentation(self):
+ """
+ L2CAP/COS/CFC/BV-02-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel, cert_channel) = self._open_channel_from_cert(
+ mtu=1000, mps=202)
+ dut_channel.send(b'hello' * 40)
+ assertThat(cert_channel).emits(
+ L2capMatchers.FirstLeIFrame(b'hello' * 40, sdu_size=200))
+
+ def test_reassembling(self):
+ """
+ L2CAP/COS/CFC/BV-03-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel, cert_channel) = self._open_channel_from_cert()
+ sdu_size_for_two_sample_packet = 12
+ cert_channel.send_first_le_i_frame(sdu_size_for_two_sample_packet,
+ SAMPLE_PACKET)
+ cert_channel.send(SAMPLE_PACKET)
+ assertThat(dut_channel).emits(
+ L2capMatchers.PacketPayloadRawData(b'\x01\x01\x02\x00\x00\x00' * 2))
+
+ def test_data_receiving(self):
+ """
+ L2CAP/COS/CFC/BV-04-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel, cert_channel) = self._open_channel_from_cert()
+ cert_channel.send_first_le_i_frame(6, SAMPLE_PACKET)
+ assertThat(dut_channel).emits(
+ L2capMatchers.PacketPayloadRawData(b'\x01\x01\x02\x00\x00\x00'))
+
+ def test_multiple_channels_with_interleaved_data_streams(self):
+ """
+ L2CAP/COS/CFC/BV-05-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel_x, cert_channel_x) = self._open_channel_from_cert(
+ signal_id=1, scid=0x0103, psm=0x33)
+ (dut_channel_y, cert_channel_y) = self._open_channel_from_cert(
+ signal_id=2, scid=0x0105, psm=0x35)
+ (dut_channel_z, cert_channel_z) = self._open_channel_from_cert(
+ signal_id=3, scid=0x0107, psm=0x37)
+ cert_channel_y.send_first_le_i_frame(6, SAMPLE_PACKET)
+ cert_channel_z.send_first_le_i_frame(6, SAMPLE_PACKET)
+ cert_channel_y.send_first_le_i_frame(6, SAMPLE_PACKET)
+ cert_channel_z.send_first_le_i_frame(6, SAMPLE_PACKET)
+ cert_channel_y.send_first_le_i_frame(6, SAMPLE_PACKET)
+ # TODO: We should assert two events in order, but it got stuck
+ assertThat(dut_channel_y).emits(
+ L2capMatchers.PacketPayloadRawData(b'\x01\x01\x02\x00\x00\x00'),
+ at_least_times=3)
+ assertThat(dut_channel_z).emits(
+ L2capMatchers.PacketPayloadRawData(b'\x01\x01\x02\x00\x00\x00'),
+ L2capMatchers.PacketPayloadRawData(
+ b'\x01\x01\x02\x00\x00\x00')).inOrder()
+ cert_channel_z.send_first_le_i_frame(6, SAMPLE_PACKET)
+ assertThat(dut_channel_z).emits(
+ L2capMatchers.PacketPayloadRawData(b'\x01\x01\x02\x00\x00\x00'))
+
+ def test_reject_unknown_command_in_le_sigling_channel(self):
+ """
+ L2CAP/LE/REJ/BI-01-C
+ """
+ self._setup_link_from_cert()
+ self.cert_l2cap.get_control_channel().send(
+ l2cap_packets.InformationRequestBuilder(
+ 2, l2cap_packets.InformationRequestInfoType.
+ EXTENDED_FEATURES_SUPPORTED))
+ assertThat(self.cert_l2cap.get_control_channel()).emits(
+ L2capMatchers.LeCommandReject())
+
+ def test_credit_based_connection_request_unsupported_le_psm(self):
+ """
+ L2CAP/LE/CFC/BV-05-C
+ """
+ self._setup_link_from_cert()
+ self.cert_l2cap.get_control_channel().send(
+ l2cap_packets.LeCreditBasedConnectionRequestBuilder(
+ 1, 0x34, 0x0101, 2000, 1000, 1000))
+ assertThat(self.cert_l2cap.get_control_channel()).emits(
+ L2capMatchers.CreditBasedConnectionResponse(
+ 0x0101,
+ result=LeCreditBasedConnectionResponseResult.
+ LE_PSM_NOT_SUPPORTED))
+
+ def test_credit_exchange_receiving_incremental_credits(self):
+ """
+ L2CAP/LE/CFC/BV-06-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel,
+ cert_channel) = self._open_channel_from_cert(initial_credit=0)
+ for _ in range(4):
+ dut_channel.send(b'hello')
+ cert_channel.send_credits(1)
+ assertThat(cert_channel).emits(
+ L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+ cert_channel.send_credits(1)
+ assertThat(cert_channel).emits(
+ L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+ cert_channel.send_credits(2)
+ assertThat(cert_channel).emits(
+ L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5),
+ L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+
+ def test_le_credit_based_connection_request_legacy_peer(self):
+ """
+ L2CAP/LE/CFC/BV-01-C
+ """
+ self._setup_link_from_cert()
+ response_future = self.dut_l2cap.connect_coc_to_cert(psm=0x33)
+ self.cert_l2cap.verify_and_reject_open_channel_from_remote(psm=0x33)
+ assertThat(response_future.get_status()).isNotEqualTo(
+ LeCreditBasedConnectionResponseResult.SUCCESS)
+
+ def test_le_credit_based_connection_request_on_supported_le_psm(self):
+ """
+ L2CAP/LE/CFC/BV-02-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel, cert_channel) = self._open_channel_from_dut()
+ cert_channel.send_first_le_i_frame(6, SAMPLE_PACKET)
+ assertThat(dut_channel).emits(
+ L2capMatchers.PacketPayloadRawData(b'\x01\x01\x02\x00\x00\x00'))
+
+ def test_credit_based_connection_response_on_supported_le_psm(self):
+ """
+ L2CAP/LE/CFC/BV-03-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel, cert_channel) = self._open_channel_from_cert()
+ dut_channel.send(b'hello')
+ assertThat(cert_channel).emits(
+ L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+
+ def test_credit_based_connection_request_on_an_unsupported_le_psm(self):
+ """
+ L2CAP/LE/CFC/BV-04-C
+ """
+ self._setup_link_from_cert()
+ response_future = self.dut_l2cap.connect_coc_to_cert(psm=0x33)
+ self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+ psm=0x33,
+ result=LeCreditBasedConnectionResponseResult.LE_PSM_NOT_SUPPORTED)
+ assertThat(response_future.get_status()).isEqualTo(
+ LeCreditBasedConnectionResponseResult.LE_PSM_NOT_SUPPORTED)
+
+ def test_request_refused_due_to_unacceptable_parameters_initiator(self):
+ """
+ L2CAP/LE/CFC/BV-21-C
+ """
+ self._setup_link_from_cert()
+ response_future = self.dut_l2cap.connect_coc_to_cert(psm=0x33)
+ self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+ psm=0x33,
+ result=LeCreditBasedConnectionResponseResult.UNACCEPTABLE_PARAMETERS
+ )
+ assertThat(response_future.get_status()).isEqualTo(
+ LeCreditBasedConnectionResponseResult.UNACCEPTABLE_PARAMETERS)
+
+ def test_credit_exchange_exceed_initial_credits(self):
+ """
+ L2CAP/LE/CFC/BI-01-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel, cert_channel) = self._open_channel_from_cert()
+ cert_channel.send_credits(65535)
+ cert_channel.verify_disconnect_request()
+
+ def test_disconnection_response(self):
+ """
+ L2CAP/LE/CFC/BV-09-C
+ """
+ self._setup_link_from_cert()
+ (dut_channel, cert_channel) = self._open_channel_from_cert()
+ cert_channel.disconnect_and_verify()
diff --git a/gd/l2cap/le/dynamic_channel_manager.h b/gd/l2cap/le/dynamic_channel_manager.h
index b57ec2c..a1b99a6 100644
--- a/gd/l2cap/le/dynamic_channel_manager.h
+++ b/gd/l2cap/le/dynamic_channel_manager.h
@@ -50,7 +50,8 @@
struct ConnectionResult {
ConnectionResultCode connection_result_code = ConnectionResultCode::SUCCESS;
hci::ErrorCode hci_error = hci::ErrorCode::SUCCESS;
- ConnectionResponseResult l2cap_connection_response_result = ConnectionResponseResult::SUCCESS;
+ LeCreditBasedConnectionResponseResult l2cap_connection_response_result =
+ LeCreditBasedConnectionResponseResult::SUCCESS;
};
/**
* OnConnectionFailureCallback(std::string failure_reason);
diff --git a/gd/l2cap/le/facade.cc b/gd/l2cap/le/facade.cc
new file mode 100644
index 0000000..168d957
--- /dev/null
+++ b/gd/l2cap/le/facade.cc
@@ -0,0 +1,263 @@
+/*
+ * 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 "l2cap/le/facade.h"
+
+#include "grpc/grpc_event_queue.h"
+#include "l2cap/le/dynamic_channel.h"
+#include "l2cap/le/dynamic_channel_manager.h"
+#include "l2cap/le/dynamic_channel_service.h"
+#include "l2cap/le/facade.grpc.pb.h"
+#include "l2cap/le/l2cap_le_module.h"
+#include "l2cap/psm.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+class L2capLeModuleFacadeService : public L2capLeModuleFacade::Service {
+ public:
+ L2capLeModuleFacadeService(L2capLeModule* l2cap_layer, os::Handler* facade_handler)
+ : l2cap_layer_(l2cap_layer), facade_handler_(facade_handler) {
+ ASSERT(l2cap_layer_ != nullptr);
+ ASSERT(facade_handler_ != nullptr);
+ }
+
+ ::grpc::Status FetchL2capData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+ ::grpc::ServerWriter<::bluetooth::l2cap::le::L2capPacket>* writer) override {
+ return pending_l2cap_data_.RunLoop(context, writer);
+ }
+
+ ::grpc::Status OpenDynamicChannel(::grpc::ServerContext* context, const OpenDynamicChannelRequest* request,
+ OpenDynamicChannelResponse* response) override {
+ auto service_helper = dynamic_channel_helper_map_.find(request->psm());
+ if (service_helper == dynamic_channel_helper_map_.end()) {
+ return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+ }
+ request->remote();
+ hci::Address peer_address;
+ ASSERT(hci::Address::FromString(request->remote().address().address(), peer_address));
+ // TODO: Support different address type
+ hci::AddressWithType peer(peer_address, hci::AddressType::RANDOM_DEVICE_ADDRESS);
+ service_helper->second->Connect(peer);
+ response->set_status(
+ static_cast<int>(service_helper->second->channel_open_fail_reason_.l2cap_connection_response_result));
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status CloseDynamicChannel(::grpc::ServerContext* context, const CloseDynamicChannelRequest* request,
+ ::google::protobuf::Empty* response) override {
+ auto service_helper = dynamic_channel_helper_map_.find(request->psm());
+ if (service_helper == dynamic_channel_helper_map_.end()) {
+ return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+ }
+ if (service_helper->second->channel_ == nullptr) {
+ return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
+ }
+ service_helper->second->channel_->Close();
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status SetDynamicChannel(::grpc::ServerContext* context,
+ const ::bluetooth::l2cap::le::SetEnableDynamicChannelRequest* request,
+ ::google::protobuf::Empty* response) override {
+ if (request->enable()) {
+ dynamic_channel_helper_map_.emplace(request->psm(), std::make_unique<L2capDynamicChannelHelper>(
+ this, l2cap_layer_, facade_handler_, request->psm()));
+ return ::grpc::Status::OK;
+ } else {
+ auto service_helper = dynamic_channel_helper_map_.find(request->psm());
+ if (service_helper == dynamic_channel_helper_map_.end()) {
+ return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+ }
+ service_helper->second->service_->Unregister(common::BindOnce([] {}), facade_handler_);
+ return ::grpc::Status::OK;
+ }
+ }
+
+ ::grpc::Status SendDynamicChannelPacket(::grpc::ServerContext* context,
+ const ::bluetooth::l2cap::le::DynamicChannelPacket* request,
+ ::google::protobuf::Empty* response) override {
+ std::unique_lock<std::mutex> lock(channel_map_mutex_);
+ if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
+ return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+ }
+ std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
+ if (!dynamic_channel_helper_map_[request->psm()]->SendPacket(packet)) {
+ return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
+ }
+ return ::grpc::Status::OK;
+ }
+
+ class L2capDynamicChannelHelper {
+ public:
+ L2capDynamicChannelHelper(L2capLeModuleFacadeService* service, L2capLeModule* l2cap_layer, os::Handler* handler,
+ Psm psm)
+ : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm) {
+ dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager();
+ dynamic_channel_manager_->RegisterService(
+ psm, {}, {},
+ common::BindOnce(&L2capDynamicChannelHelper::on_l2cap_service_registration_complete,
+ common::Unretained(this)),
+ common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)), handler_);
+ }
+
+ ~L2capDynamicChannelHelper() {
+ if (channel_ != nullptr) {
+ channel_->GetQueueUpEnd()->UnregisterDequeue();
+ channel_ = nullptr;
+ }
+ }
+
+ void Connect(hci::AddressWithType address) {
+ dynamic_channel_manager_->ConnectChannel(
+ address, {}, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
+ common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
+ std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+ if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+ LOG_WARN("Channel is not open for psm %d", psm_);
+ }
+ }
+
+ void disconnect() {
+ channel_->Close();
+ }
+
+ void on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult registration_result,
+ std::unique_ptr<DynamicChannelService> service) {
+ if (registration_result != DynamicChannelManager::RegistrationResult::SUCCESS) {
+ LOG_ERROR("Service registration failed");
+ } else {
+ service_ = std::move(service);
+ }
+ }
+
+ // invoked from Facade Handler
+ void on_connection_open(std::unique_ptr<DynamicChannel> channel) {
+ {
+ std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+ channel_ = std::move(channel);
+ }
+ channel_open_cv_.notify_all();
+ channel_->RegisterOnCloseCallback(
+ facade_service_->facade_handler_,
+ common::BindOnce(&L2capDynamicChannelHelper::on_close_callback, common::Unretained(this)));
+ channel_->GetQueueUpEnd()->RegisterDequeue(
+ facade_service_->facade_handler_,
+ common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
+ }
+
+ void on_close_callback(hci::ErrorCode error_code) {
+ {
+ std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+ channel_->GetQueueUpEnd()->UnregisterDequeue();
+ }
+ channel_ = nullptr;
+ }
+
+ void on_connect_fail(DynamicChannelManager::ConnectionResult result) {
+ {
+ std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+ channel_ = nullptr;
+ channel_open_fail_reason_ = result;
+ }
+ channel_open_cv_.notify_all();
+ }
+
+ void on_incoming_packet() {
+ auto packet = channel_->GetQueueUpEnd()->TryDequeue();
+ std::string data = std::string(packet->begin(), packet->end());
+ L2capPacket l2cap_data;
+ l2cap_data.set_psm(psm_);
+ l2cap_data.set_payload(data);
+ facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
+ }
+
+ bool SendPacket(std::vector<uint8_t> packet) {
+ if (channel_ == nullptr) {
+ std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+ if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+ LOG_WARN("Channel is not open for psm %d", psm_);
+ return false;
+ }
+ }
+ std::promise<void> promise;
+ auto future = promise.get_future();
+ channel_->GetQueueUpEnd()->RegisterEnqueue(
+ handler_, common::Bind(&L2capDynamicChannelHelper::enqueue_callback, common::Unretained(this), packet,
+ common::Passed(std::move(promise))));
+ auto status = future.wait_for(std::chrono::milliseconds(500));
+ if (status != std::future_status::ready) {
+ LOG_ERROR("Can't send packet because the previous packet wasn't sent yet");
+ return false;
+ }
+ return true;
+ }
+
+ std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet,
+ std::promise<void> promise) {
+ auto packet_one = std::make_unique<packet::RawBuilder>(2000);
+ packet_one->AddOctets(packet);
+ channel_->GetQueueUpEnd()->UnregisterEnqueue();
+ promise.set_value();
+ return packet_one;
+ };
+
+ L2capLeModuleFacadeService* facade_service_;
+ L2capLeModule* l2cap_layer_;
+ os::Handler* handler_;
+ std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_;
+ std::unique_ptr<DynamicChannelService> service_;
+ std::unique_ptr<DynamicChannel> channel_ = nullptr;
+ Psm psm_;
+ DynamicChannelManager::ConnectionResult channel_open_fail_reason_;
+ std::condition_variable channel_open_cv_;
+ std::mutex channel_open_cv_mutex_;
+ };
+
+ L2capLeModule* l2cap_layer_;
+ os::Handler* facade_handler_;
+ std::mutex channel_map_mutex_;
+ std::map<Psm, std::unique_ptr<L2capDynamicChannelHelper>> dynamic_channel_helper_map_;
+ ::bluetooth::grpc::GrpcEventQueue<L2capPacket> pending_l2cap_data_{"FetchL2capData"};
+};
+
+void L2capLeModuleFacadeModule::ListDependencies(ModuleList* list) {
+ ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+ list->add<l2cap::le::L2capLeModule>();
+}
+
+void L2capLeModuleFacadeModule::Start() {
+ ::bluetooth::grpc::GrpcFacadeModule::Start();
+ service_ = new L2capLeModuleFacadeService(GetDependency<l2cap::le::L2capLeModule>(), GetHandler());
+}
+
+void L2capLeModuleFacadeModule::Stop() {
+ delete service_;
+ ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* L2capLeModuleFacadeModule::GetService() const {
+ return service_;
+}
+
+const ModuleFactory L2capLeModuleFacadeModule::Factory =
+ ::bluetooth::ModuleFactory([]() { return new L2capLeModuleFacadeModule(); });
+
+} // namespace le
+} // namespace l2cap
+} // namespace bluetooth
diff --git a/gd/l2cap/le/facade.h b/gd/l2cap/le/facade.h
new file mode 100644
index 0000000..0f811c8
--- /dev/null
+++ b/gd/l2cap/le/facade.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+class L2capLeModuleFacadeService;
+
+class L2capLeModuleFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+ static const ModuleFactory Factory;
+
+ void ListDependencies(ModuleList* list) override;
+
+ void Start() override;
+
+ void Stop() override;
+
+ ::grpc::Service* GetService() const override;
+
+ private:
+ L2capLeModuleFacadeService* service_;
+};
+
+} // namespace le
+} // namespace l2cap
+} // namespace bluetooth
diff --git a/gd/l2cap/le/facade.proto b/gd/l2cap/le/facade.proto
new file mode 100644
index 0000000..2a1aeae
--- /dev/null
+++ b/gd/l2cap/le/facade.proto
@@ -0,0 +1,51 @@
+syntax = "proto3";
+
+package bluetooth.l2cap.le;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service L2capLeModuleFacade {
+ rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {}
+ rpc FetchDynamicChannelOpenEvent(google.protobuf.Empty) returns (stream DynamicChannelOpenEvent) {}
+ // Initiate a credit based connection request and block until response is received for up to some timeout (2s)
+ rpc OpenDynamicChannel(OpenDynamicChannelRequest) returns (OpenDynamicChannelResponse) {}
+ rpc CloseDynamicChannel(CloseDynamicChannelRequest) returns (google.protobuf.Empty) {}
+ rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {}
+ rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {}
+}
+
+message L2capPacket {
+ uint32 psm = 1;
+ bytes payload = 2;
+}
+
+message DynamicChannelOpenEvent {
+ uint32 psm = 1;
+ uint32 connection_response_result = 2;
+}
+
+message OpenDynamicChannelRequest {
+ facade.BluetoothAddressWithType remote = 1;
+ uint32 psm = 2;
+}
+
+message OpenDynamicChannelResponse {
+ uint32 status = 1;
+}
+
+message CloseDynamicChannelRequest {
+ facade.BluetoothAddressWithType remote = 1;
+ uint32 psm = 2;
+}
+
+message SetEnableDynamicChannelRequest {
+ uint32 psm = 1;
+ bool enable = 2;
+}
+
+message DynamicChannelPacket {
+ facade.BluetoothAddressWithType remote = 1;
+ uint32 psm = 2;
+ bytes payload = 3;
+}
diff --git a/gd/l2cap/le/fixed_channel_manager.h b/gd/l2cap/le/fixed_channel_manager.h
index 4583310..17410e4 100644
--- a/gd/l2cap/le/fixed_channel_manager.h
+++ b/gd/l2cap/le/fixed_channel_manager.h
@@ -17,7 +17,6 @@
#include <string>
-#include "hci/acl_manager.h"
#include "hci/address_with_type.h"
#include "l2cap/cid.h"
#include "l2cap/le/fixed_channel.h"
diff --git a/gd/l2cap/le/internal/link.cc b/gd/l2cap/le/internal/link.cc
index 37305fb..adeaec3 100644
--- a/gd/l2cap/le/internal/link.cc
+++ b/gd/l2cap/le/internal/link.cc
@@ -30,6 +30,9 @@
namespace le {
namespace internal {
+static constexpr uint16_t kDefaultMinimumCeLength = 0x0002;
+static constexpr uint16_t kDefaultMaximumCeLength = 0x0C00;
+
Link::Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
l2cap::internal::ParameterProvider* parameter_provider,
DynamicChannelServiceManagerImpl* dynamic_service_manager,
@@ -55,13 +58,29 @@
acl_connection_->Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
}
-void Link::UpdateConnectionParameter(SignalId signal_id, uint16_t conn_interval_min, uint16_t conn_interval_max,
- uint16_t conn_latency, uint16_t supervision_timeout) {
+void Link::UpdateConnectionParameterFromRemote(SignalId signal_id, uint16_t conn_interval_min,
+ uint16_t conn_interval_max, uint16_t conn_latency,
+ uint16_t supervision_timeout) {
acl_connection_->LeConnectionUpdate(
- conn_interval_min, conn_interval_max, conn_latency, supervision_timeout,
+ conn_interval_min, conn_interval_max, conn_latency, supervision_timeout, kDefaultMinimumCeLength,
+ kDefaultMaximumCeLength,
common::BindOnce(&Link::on_connection_update_complete, common::Unretained(this), signal_id), l2cap_handler_);
}
+void Link::SendConnectionParameterUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
+ uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length) {
+ if (acl_connection_->GetRole() == hci::Role::SLAVE) {
+ // TODO: If both LL master and slave support 4.1, use HCI command directly
+ signalling_manager_.SendConnectionParameterUpdateRequest(conn_interval_min, conn_interval_max, conn_latency,
+ supervision_timeout);
+ return;
+ }
+ acl_connection_->LeConnectionUpdate(
+ conn_interval_min, conn_interval_max, conn_latency, supervision_timeout, min_ce_length, max_ce_length,
+ common::BindOnce(&Link::on_connection_update_complete, common::Unretained(this), kInvalidSignalId),
+ l2cap_handler_);
+}
+
std::shared_ptr<FixedChannelImpl> Link::AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
auto channel = fixed_channel_allocator_.AllocateChannel(cid, security_policy);
data_pipeline_manager_.AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
@@ -82,7 +101,9 @@
return;
}
auto reserved_cid = ReserveDynamicChannel();
- signalling_manager_.SendConnectionRequest(psm, reserved_cid, pending_dynamic_channel_connection.configuration_.mtu);
+ auto mtu = pending_dynamic_channel_connection.configuration_.mtu;
+ local_cid_to_pending_dynamic_channel_connection_map_[reserved_cid] = std::move(pending_dynamic_channel_connection);
+ signalling_manager_.SendConnectionRequest(psm, reserved_cid, mtu);
}
void Link::SendDisconnectionRequest(Cid local_cid, Cid remote_cid) {
@@ -93,8 +114,18 @@
signalling_manager_.SendDisconnectRequest(local_cid, remote_cid);
}
-void Link::OnOutgoingConnectionRequestFail(Cid local_cid) {
- local_cid_to_pending_dynamic_channel_connection_map_.erase(local_cid);
+void Link::OnOutgoingConnectionRequestFail(Cid local_cid, LeCreditBasedConnectionResponseResult response_result) {
+ if (local_cid_to_pending_dynamic_channel_connection_map_.find(local_cid) !=
+ local_cid_to_pending_dynamic_channel_connection_map_.end()) {
+ // TODO(hsz): Currently we only notify the client when the remote didn't send connection response SUCCESS.
+ // Should we notify the client when the link failed to establish?
+ DynamicChannelManager::ConnectionResult result{
+ .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR,
+ .hci_error = hci::ErrorCode::SUCCESS,
+ .l2cap_connection_response_result = response_result,
+ };
+ NotifyChannelFail(local_cid, result);
+ }
dynamic_channel_allocator_.FreeChannel(local_cid);
}
@@ -122,12 +153,6 @@
return channel;
}
-DynamicChannelConfigurationOption Link::GetConfigurationForInitialConfiguration(Cid cid) {
- ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
- local_cid_to_pending_dynamic_channel_connection_map_.end());
- return local_cid_to_pending_dynamic_channel_connection_map_[cid].configuration_;
-}
-
void Link::FreeDynamicChannel(Cid cid) {
if (dynamic_channel_allocator_.FindChannelByCid(cid) == nullptr) {
return;
@@ -159,12 +184,11 @@
local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
}
-void Link::NotifyChannelFail(Cid cid) {
+void Link::NotifyChannelFail(Cid cid, DynamicChannelManager::ConnectionResult result) {
ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
local_cid_to_pending_dynamic_channel_connection_map_.end());
auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
// TODO(cmanton) Pass proper connection falure result to user
- DynamicChannelManager::ConnectionResult result;
pending_dynamic_channel_connection.handler_->Post(
common::BindOnce(std::move(pending_dynamic_channel_connection.on_fail_callback_), result));
local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
@@ -183,6 +207,9 @@
}
void Link::on_connection_update_complete(SignalId signal_id, hci::ErrorCode error_code) {
+ if (!signal_id.IsValid()) {
+ return;
+ }
ConnectionParameterUpdateResponseResult result = (error_code == hci::ErrorCode::SUCCESS)
? ConnectionParameterUpdateResponseResult::ACCEPTED
: ConnectionParameterUpdateResponseResult::REJECTED;
diff --git a/gd/l2cap/le/internal/link.h b/gd/l2cap/le/internal/link.h
index 3c90856..be63d69 100644
--- a/gd/l2cap/le/internal/link.h
+++ b/gd/l2cap/le/internal/link.h
@@ -72,8 +72,13 @@
virtual void Disconnect();
// Handles connection parameter update request from remote
- virtual void UpdateConnectionParameter(SignalId signal_id, uint16_t conn_interval_min, uint16_t conn_interval_max,
- uint16_t conn_latency, uint16_t supervision_timeout);
+ virtual void UpdateConnectionParameterFromRemote(SignalId signal_id, uint16_t conn_interval_min,
+ uint16_t conn_interval_max, uint16_t conn_latency,
+ uint16_t supervision_timeout);
+
+ virtual void SendConnectionParameterUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max,
+ uint16_t conn_latency, uint16_t supervision_timeout,
+ uint16_t min_ce_length, uint16_t max_ce_length);
// FixedChannel methods
@@ -90,7 +95,7 @@
void SendDisconnectionRequest(Cid local_cid, Cid remote_cid) override;
// Invoked by signalling manager to indicate an outgoing connection request failed and link shall free resources
- virtual void OnOutgoingConnectionRequestFail(Cid local_cid);
+ virtual void OnOutgoingConnectionRequestFail(Cid local_cid, LeCreditBasedConnectionResponseResult result);
virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateDynamicChannel(Psm psm, Cid remote_cid,
SecurityPolicy security_policy);
@@ -98,15 +103,13 @@
virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateReservedDynamicChannel(
Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy);
- virtual DynamicChannelConfigurationOption GetConfigurationForInitialConfiguration(Cid cid);
-
virtual void FreeDynamicChannel(Cid cid);
// Check how many channels are acquired or in use, if zero, start tear down timer, if non-zero, cancel tear down timer
virtual void RefreshRefCount();
void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> user_channel);
- void NotifyChannelFail(Cid cid);
+ void NotifyChannelFail(Cid cid, DynamicChannelManager::ConnectionResult result);
virtual std::string ToString() {
return GetDevice().ToString();
@@ -131,6 +134,9 @@
os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
DISALLOW_COPY_AND_ASSIGN(Link);
+ // Received connection update complete from ACL manager. SignalId is bound to a valid number when we need to send a
+ // response to remote. If SignalId is bound to an invalid number, we don't send a response to remote, because the
+ // connection update request is not from remote LL slave.
void on_connection_update_complete(SignalId signal_id, hci::ErrorCode error_code);
};
diff --git a/gd/l2cap/le/internal/link_manager.h b/gd/l2cap/le/internal/link_manager.h
index 9e9c0e8..b759e7b 100644
--- a/gd/l2cap/le/internal/link_manager.h
+++ b/gd/l2cap/le/internal/link_manager.h
@@ -28,6 +28,7 @@
#include "l2cap/internal/parameter_provider.h"
#include "l2cap/internal/scheduler.h"
#include "l2cap/le/fixed_channel_manager.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
#include "l2cap/le/internal/link.h"
@@ -39,9 +40,10 @@
class LinkManager : public hci::LeConnectionCallbacks {
public:
LinkManager(os::Handler* l2cap_handler, hci::AclManager* acl_manager, FixedChannelServiceManagerImpl* service_manager,
+ DynamicChannelServiceManagerImpl* dynamic_service_manager,
l2cap::internal::ParameterProvider* parameter_provider)
: l2cap_handler_(l2cap_handler), acl_manager_(acl_manager), fixed_channel_service_manager_(service_manager),
- parameter_provider_(parameter_provider) {
+ dynamic_channel_service_manager_(dynamic_service_manager), parameter_provider_(parameter_provider) {
acl_manager_->RegisterLeCallbacks(this, l2cap_handler_);
}
diff --git a/gd/l2cap/le/internal/link_manager_test.cc b/gd/l2cap/le/internal/link_manager_test.cc
index cbef3f3..8360c51 100644
--- a/gd/l2cap/le/internal/link_manager_test.cc
+++ b/gd/l2cap/le/internal/link_manager_test.cc
@@ -96,7 +96,7 @@
os::Handler* hci_callback_handler = nullptr;
EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
- LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+ LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
mock_parameter_provider_);
EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -194,7 +194,7 @@
os::Handler* hci_callback_handler = nullptr;
EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
- LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+ LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
mock_parameter_provider_);
EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -229,7 +229,7 @@
os::Handler* hci_callback_handler = nullptr;
EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
- LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+ LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
mock_parameter_provider_);
EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -276,7 +276,7 @@
os::Handler* hci_callback_handler = nullptr;
EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
- LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+ LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
mock_parameter_provider_);
EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -358,7 +358,7 @@
os::Handler* hci_callback_handler = nullptr;
EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
- LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+ LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
mock_parameter_provider_);
EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -442,7 +442,7 @@
os::Handler* hci_callback_handler = nullptr;
EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
- LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+ LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
mock_parameter_provider_);
EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
diff --git a/gd/l2cap/le/internal/signalling_manager.cc b/gd/l2cap/le/internal/signalling_manager.cc
index c9d978c..59dbdb1 100644
--- a/gd/l2cap/le/internal/signalling_manager.cc
+++ b/gd/l2cap/le/internal/signalling_manager.cc
@@ -56,9 +56,8 @@
}
void LeSignallingManager::SendConnectionRequest(Psm psm, Cid local_cid, Mtu mtu) {
- PendingCommand pending_command = {
- next_signal_id_, LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST, psm, local_cid, {}, mtu, link_->GetMps(),
- link_->GetInitialCredit()};
+ PendingCommand pending_command = PendingCommand::CreditBasedConnectionRequest(
+ next_signal_id_, psm, local_cid, mtu, link_->GetMps(), link_->GetInitialCredit());
next_signal_id_++;
pending_commands_.push(pending_command);
if (pending_commands_.size() == 1) {
@@ -66,9 +65,8 @@
}
}
-void LeSignallingManager::SendDisconnectRequest(Cid local_cid, Cid remote_cid) {
- PendingCommand pending_command = {
- next_signal_id_, LeCommandCode::DISCONNECTION_REQUEST, {}, local_cid, remote_cid, {}, {}, {}};
+void LeSignallingManager::SendDisconnectRequest(Cid scid, Cid dcid) {
+ PendingCommand pending_command = PendingCommand::DisconnectionRequest(next_signal_id_, scid, dcid);
next_signal_id_++;
pending_commands_.push(pending_command);
if (pending_commands_.size() == 1) {
@@ -78,7 +76,13 @@
void LeSignallingManager::SendConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max,
uint16_t slave_latency, uint16_t timeout_multiplier) {
- LOG_ERROR("Not implemented");
+ PendingCommand pending_command = PendingCommand::ConnectionParameterUpdate(
+ next_signal_id_, interval_min, interval_max, slave_latency, timeout_multiplier);
+ next_signal_id_++;
+ pending_commands_.push(pending_command);
+ if (pending_commands_.size() == 1) {
+ handle_send_next_command();
+ }
}
void LeSignallingManager::SendConnectionParameterUpdateResponse(SignalId signal_id,
@@ -99,23 +103,55 @@
void LeSignallingManager::OnCommandReject(LeCommandRejectView command_reject_view) {
auto signal_id = command_reject_view.GetIdentifier();
- if (signal_id != command_just_sent_.signal_id_ || command_just_sent_.command_code_ != command_reject_view.GetCode()) {
+ if (signal_id != command_just_sent_.signal_id_) {
LOG_WARN("Unexpected response: no pending request");
return;
}
alarm_.Cancel();
+ if (command_just_sent_.command_code_ == LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST) {
+ link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
+ LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
+ }
handle_send_next_command();
LOG_WARN("Command rejected");
}
-void LeSignallingManager::OnConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max,
- uint16_t slave_latency, uint16_t timeout_multiplier) {
- LOG_ERROR("Not implemented");
+void LeSignallingManager::OnConnectionParameterUpdateRequest(SignalId signal_id, uint16_t interval_min,
+ uint16_t interval_max, uint16_t slave_latency,
+ uint16_t timeout_multiplier) {
+ if (link_->GetRole() == hci::Role::SLAVE) {
+ LOG_WARN("Received request from LL master");
+ auto builder = LeCommandRejectNotUnderstoodBuilder::Create(signal_id.Value());
+ enqueue_buffer_->Enqueue(std::move(builder), handler_);
+ return;
+ }
+ if (interval_min < 6 || interval_min > 3200 || interval_max < 6 || interval_max > 3200 || slave_latency >= 500 ||
+ timeout_multiplier < 10 || timeout_multiplier > 3200) {
+ LOG_WARN("Received invalid connection parameter update request from LL master");
+ auto builder = ConnectionParameterUpdateResponseBuilder::Create(signal_id.Value(),
+ ConnectionParameterUpdateResponseResult::REJECTED);
+ enqueue_buffer_->Enqueue(std::move(builder), handler_);
+ return;
+ }
+ link_->UpdateConnectionParameterFromRemote(signal_id, interval_min, interval_max, slave_latency, timeout_multiplier);
}
-void LeSignallingManager::OnConnectionParameterUpdateResponse(ConnectionParameterUpdateResponseResult result) {
- LOG_ERROR("Not implemented");
+void LeSignallingManager::OnConnectionParameterUpdateResponse(SignalId signal_id,
+ ConnectionParameterUpdateResponseResult result) {
+ if (signal_id != command_just_sent_.signal_id_) {
+ LOG_WARN("Unexpected response: no pending request");
+ return;
+ }
+ if (command_just_sent_.command_code_ != LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST) {
+ LOG_WARN("Unexpected response: no pending request");
+ return;
+ }
+ alarm_.Cancel();
+ command_just_sent_.signal_id_ = kInitialSignalId;
+ if (result != ConnectionParameterUpdateResponseResult::ACCEPTED) {
+ LOG_ERROR("Connection parameter update is not accepted");
+ }
}
void LeSignallingManager::OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid, Mtu mtu, uint16_t mps,
@@ -161,7 +197,7 @@
return;
}
- send_connection_response(signal_id, remote_cid, local_mtu, local_mps, link_->GetInitialCredit(),
+ send_connection_response(signal_id, new_channel->GetCid(), local_mtu, local_mps, link_->GetInitialCredit(),
LeCreditBasedConnectionResponseResult::SUCCESS);
auto* data_controller = reinterpret_cast<l2cap::internal::LeCreditBasedDataController*>(
data_pipeline_manager_->GetDataController(new_channel->GetCid()));
@@ -186,7 +222,7 @@
command_just_sent_.signal_id_ = kInitialSignalId;
if (result != LeCreditBasedConnectionResponseResult::SUCCESS) {
LOG_WARN("Connection failed: %s", LeCreditBasedConnectionResponseResultText(result).data());
- link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+ link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_, result);
handle_send_next_command();
return;
}
@@ -194,7 +230,8 @@
link_->AllocateReservedDynamicChannel(command_just_sent_.source_cid_, command_just_sent_.psm_, remote_cid, {});
if (new_channel == nullptr) {
LOG_WARN("Can't allocate dynamic channel");
- link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+ link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
+ LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
handle_send_next_command();
return;
}
@@ -223,7 +260,7 @@
link_->FreeDynamicChannel(cid);
}
-void LeSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid cid, Cid remote_cid) {
+void LeSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid) {
if (signal_id != command_just_sent_.signal_id_ ||
command_just_sent_.command_code_ != LeCommandCode::DISCONNECTION_REQUEST) {
LOG_WARN("Unexpected response: no pending request");
@@ -285,8 +322,9 @@
return;
}
OnConnectionParameterUpdateRequest(
- parameter_update_req_view.GetIntervalMin(), parameter_update_req_view.GetIntervalMax(),
- parameter_update_req_view.GetSlaveLatency(), parameter_update_req_view.GetTimeoutMultiplier());
+ parameter_update_req_view.GetIdentifier(), parameter_update_req_view.GetIntervalMin(),
+ parameter_update_req_view.GetIntervalMax(), parameter_update_req_view.GetSlaveLatency(),
+ parameter_update_req_view.GetTimeoutMultiplier());
return;
}
case LeCommandCode::CONNECTION_PARAMETER_UPDATE_RESPONSE: {
@@ -295,7 +333,8 @@
if (!parameter_update_rsp_view.IsValid()) {
return;
}
- OnConnectionParameterUpdateResponse(parameter_update_rsp_view.GetResult());
+ OnConnectionParameterUpdateResponse(parameter_update_rsp_view.GetIdentifier(),
+ parameter_update_rsp_view.GetResult());
return;
}
case LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST: {
@@ -372,7 +411,8 @@
}
switch (command_just_sent_.command_code_) {
case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
- link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+ link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
+ LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
break;
}
default:
@@ -405,6 +445,12 @@
alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
break;
}
+ case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
+ auto builder = ConnectionParameterUpdateRequestBuilder::Create(command_just_sent_.signal_id_.Value(), 0, 0, 0, 0);
+ enqueue_buffer_->Enqueue(std::move(builder), handler_);
+ alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
+ break;
+ }
default: {
LOG_WARN("Unsupported command code 0x%x", static_cast<int>(command_just_sent_.command_code_));
}
diff --git a/gd/l2cap/le/internal/signalling_manager.h b/gd/l2cap/le/internal/signalling_manager.h
index f218168..1f9d7d3 100644
--- a/gd/l2cap/le/internal/signalling_manager.h
+++ b/gd/l2cap/le/internal/signalling_manager.h
@@ -49,6 +49,44 @@
Mtu mtu_;
uint16_t mps_;
uint16_t credits_;
+ uint16_t interval_min_;
+ uint16_t interval_max_;
+ uint16_t slave_latency_;
+ uint16_t timeout_multiplier_;
+
+ static PendingCommand CreditBasedConnectionRequest(SignalId signal_id, Psm psm, Cid scid, Mtu mtu, uint16_t mps,
+ uint16_t initial_credits) {
+ PendingCommand pending_command;
+ pending_command.signal_id_ = signal_id;
+ pending_command.command_code_ = LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST;
+ pending_command.psm_ = psm;
+ pending_command.source_cid_ = scid;
+ pending_command.mtu_ = mtu;
+ pending_command.mps_ = mps;
+ pending_command.credits_ = initial_credits;
+ return pending_command;
+ }
+
+ static PendingCommand DisconnectionRequest(SignalId signal_id, Cid scid, Cid dcid) {
+ PendingCommand pending_command;
+ pending_command.signal_id_ = signal_id;
+ pending_command.command_code_ = LeCommandCode::DISCONNECTION_REQUEST;
+ pending_command.source_cid_ = scid;
+ pending_command.destination_cid_ = dcid;
+ return pending_command;
+ }
+
+ static PendingCommand ConnectionParameterUpdate(SignalId signal_id, uint16_t interval_min, uint16_t interval_max,
+ uint16_t slave_latency, uint16_t timeout_multiplier) {
+ PendingCommand pending_command;
+ pending_command.signal_id_ = signal_id;
+ pending_command.command_code_ = LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST;
+ pending_command.interval_min_ = interval_min;
+ pending_command.interval_max_ = interval_max;
+ pending_command.slave_latency_ = slave_latency;
+ pending_command.timeout_multiplier_ = timeout_multiplier;
+ return pending_command;
+ }
};
class Link;
@@ -65,6 +103,7 @@
void SendDisconnectRequest(Cid local_cid, Cid remote_cid);
+ // Note: Since Core 4.1, LL slave can send this through HCI command.
void SendConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency,
uint16_t timeout_multiplier);
@@ -76,9 +115,9 @@
void OnCommandReject(LeCommandRejectView command_reject_view);
- void OnConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency,
- uint16_t timeout_multiplier);
- void OnConnectionParameterUpdateResponse(ConnectionParameterUpdateResponseResult result);
+ void OnConnectionParameterUpdateRequest(SignalId signal_id, uint16_t interval_min, uint16_t interval_max,
+ uint16_t slave_latency, uint16_t timeout_multiplier);
+ void OnConnectionParameterUpdateResponse(SignalId signal_id, ConnectionParameterUpdateResponseResult result);
void OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid, Mtu mtu, uint16_t mps,
uint16_t initial_credits);
diff --git a/gd/l2cap/le/l2cap_le_module.cc b/gd/l2cap/le/l2cap_le_module.cc
index 4959547..cc7b588 100644
--- a/gd/l2cap/le/l2cap_le_module.cc
+++ b/gd/l2cap/le/l2cap_le_module.cc
@@ -13,21 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_TAG "l2cap2"
#include <memory>
-#include "common/bidi_queue.h"
#include "hci/acl_manager.h"
-#include "hci/address.h"
-#include "hci/hci_layer.h"
-#include "hci/hci_packets.h"
#include "l2cap/internal/parameter_provider.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
#include "l2cap/le/internal/link_manager.h"
#include "module.h"
#include "os/handler.h"
-#include "os/log.h"
#include "l2cap/le/l2cap_le_module.h"
@@ -44,8 +39,9 @@
hci::AclManager* acl_manager_;
l2cap::internal::ParameterProvider parameter_provider_;
internal::FixedChannelServiceManagerImpl fixed_channel_service_manager_impl_{l2cap_handler_};
+ internal::DynamicChannelServiceManagerImpl dynamic_channel_service_manager_impl_{l2cap_handler_};
internal::LinkManager link_manager_{l2cap_handler_, acl_manager_, &fixed_channel_service_manager_impl_,
- ¶meter_provider_};
+ &dynamic_channel_service_manager_impl_, ¶meter_provider_};
};
L2capLeModule::L2capLeModule() {}
@@ -72,6 +68,11 @@
&pimpl_->link_manager_, pimpl_->l2cap_handler_));
}
+std::unique_ptr<DynamicChannelManager> L2capLeModule::GetDynamicChannelManager() {
+ return std::unique_ptr<DynamicChannelManager>(new DynamicChannelManager(
+ &pimpl_->dynamic_channel_service_manager_impl_, &pimpl_->link_manager_, pimpl_->l2cap_handler_));
+}
+
} // namespace le
} // namespace l2cap
} // namespace bluetooth
diff --git a/gd/l2cap/le/l2cap_le_module.h b/gd/l2cap/le/l2cap_le_module.h
index 4ed3d9f..b9d76d5 100644
--- a/gd/l2cap/le/l2cap_le_module.h
+++ b/gd/l2cap/le/l2cap_le_module.h
@@ -17,6 +17,7 @@
#include <memory>
+#include "l2cap/le/dynamic_channel_manager.h"
#include "l2cap/le/fixed_channel_manager.h"
#include "module.h"
@@ -34,6 +35,11 @@
*/
virtual std::unique_ptr<FixedChannelManager> GetFixedChannelManager();
+ /**
+ * Get the api to the LE dynamic channel l2cap module
+ */
+ virtual std::unique_ptr<DynamicChannelManager> GetDynamicChannelManager();
+
static const ModuleFactory Factory;
protected:
diff --git a/gd/neighbor/cert/neighbor_test.py b/gd/neighbor/cert/neighbor_test.py
index b83812f..65741d8 100644
--- a/gd/neighbor/cert/neighbor_test.py
+++ b/gd/neighbor/cert/neighbor_test.py
@@ -18,7 +18,7 @@
import sys
import logging
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from google.protobuf import empty_pb2 as empty_proto
from facade import rootservice_pb2 as facade_rootservice
@@ -29,7 +29,7 @@
import bluetooth_packets_python3 as bt_packets
-class NeighborTest(GdFacadeOnlyBaseTestClass):
+class NeighborTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
diff --git a/gd/packet/parser/README b/gd/packet/parser/README
index 9006c94..6915538 100644
--- a/gd/packet/parser/README
+++ b/gd/packet/parser/README
@@ -14,11 +14,11 @@
Checksum types
checksum MyChecksumClass : 16 "path/to/the/class/"
- Checksum fields need to implement the following three static methods:
- static void Initialize(MyChecksumClass&);
- static void AddByte(MyChecksumClass&, uint8_t);
+ Checksum fields need to implement the following three methods:
+ void Initialize(MyChecksumClass&);
+ void AddByte(MyChecksumClass&, uint8_t);
// Assuming a 16-bit (uint16_t) checksum:
- static uint16_t GetChecksum(MyChecksumClass&);
+ uint16_t GetChecksum(MyChecksumClass&);
-------------
LIMITATIONS
-------------
@@ -57,3 +57,4 @@
static void Serialize(const Type&, MutableView&);
static std::optional<size_t> Size(Iterator);
static Type Parse(Iterator);
+ std::string ToString();
\ No newline at end of file
diff --git a/gd/packet/parser/custom_type_checker.h b/gd/packet/parser/custom_type_checker.h
index 5d99622..31b4660 100644
--- a/gd/packet/parser/custom_type_checker.h
+++ b/gd/packet/parser/custom_type_checker.h
@@ -36,14 +36,17 @@
template <class C, bool little_endian, std::optional<Iterator<little_endian>> (*)(C* vec, Iterator<little_endian> it)>
struct ParseChecker {};
+ template <class C, std::string (C::*)() const>
+ struct ToStringChecker {};
+
template <class C, bool little_endian>
static int Test(SerializeChecker<C, &C::Serialize>*, SizeChecker<C, &C::size>*,
- ParseChecker<C, little_endian, &C::Parse>*);
+ ParseChecker<C, little_endian, &C::Parse>*, ToStringChecker<C, &C::ToString>*);
template <class C, bool little_endian>
static char Test(...);
- static constexpr bool value = (sizeof(Test<T, packet_little_endian>(0, 0, 0)) == sizeof(int));
+ static constexpr bool value = (sizeof(Test<T, packet_little_endian>(0, 0, 0, 0)) == sizeof(int));
};
} // namespace packet
} // namespace bluetooth
diff --git a/gd/packet/parser/fields/array_field.cc b/gd/packet/parser/fields/array_field.cc
index 37dea16..c66252e 100644
--- a/gd/packet/parser/fields/array_field.cc
+++ b/gd/packet/parser/fields/array_field.cc
@@ -175,3 +175,23 @@
const PacketField* ArrayField::GetElementField() const {
return element_field_;
}
+
+void ArrayField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << "\"ARRAY[\";";
+ s << "/* " << element_field_->GetDataType() << " " << element_field_->GetFieldType() << " */";
+
+ std::string arr_idx = "arridx_" + accessor;
+ std::string arr_size = std::to_string(array_size_);
+ s << "for (size_t index = 0; index < " << arr_size << "; index++) {";
+ std::string element_accessor = "(" + accessor + "[index])";
+ s << "ss << ((index == 0) ? \"\" : \", \") << ";
+
+ if (element_field_->GetFieldType() == CustomField::kFieldType) {
+ s << element_accessor << ".ToString()";
+ } else {
+ element_field_->GenStringRepresentation(s, element_accessor);
+ }
+
+ s << ";}";
+ s << "ss << \"]\"";
+}
diff --git a/gd/packet/parser/fields/array_field.h b/gd/packet/parser/fields/array_field.h
index dd49f26..e31e8de 100644
--- a/gd/packet/parser/fields/array_field.h
+++ b/gd/packet/parser/fields/array_field.h
@@ -62,6 +62,8 @@
virtual const PacketField* GetElementField() const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
const std::string name_;
const PacketField* element_field_{nullptr};
diff --git a/gd/packet/parser/fields/body_field.cc b/gd/packet/parser/fields/body_field.cc
index 30abf6e..632456f 100644
--- a/gd/packet/parser/fields/body_field.cc
+++ b/gd/packet/parser/fields/body_field.cc
@@ -71,3 +71,7 @@
void BodyField::GenValidator(std::ostream&) const {
// Do nothing
}
+
+void BodyField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << "\"BODY REPRENTATION_UNIMPLEMENTED " << accessor << " \"";
+}
diff --git a/gd/packet/parser/fields/body_field.h b/gd/packet/parser/fields/body_field.h
index 656a935..d8c4ee0 100644
--- a/gd/packet/parser/fields/body_field.h
+++ b/gd/packet/parser/fields/body_field.h
@@ -50,6 +50,8 @@
virtual void GenValidator(std::ostream&) const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
// Body fields can only be dynamically sized.
const SizeField* size_field_{nullptr};
};
diff --git a/gd/packet/parser/fields/checksum_field.cc b/gd/packet/parser/fields/checksum_field.cc
index 4647992..67863c2 100644
--- a/gd/packet/parser/fields/checksum_field.cc
+++ b/gd/packet/parser/fields/checksum_field.cc
@@ -58,3 +58,8 @@
void ChecksumField::GenValidator(std::ostream&) const {
// Done in packet_def.cc
}
+
+void ChecksumField::GenStringRepresentation(std::ostream& s, std::string) const {
+ // TODO: there is currently no way to get checksum value
+ s << "\"CHECKSUM\"";
+}
diff --git a/gd/packet/parser/fields/checksum_field.h b/gd/packet/parser/fields/checksum_field.h
index c15f023..0e0a4c2 100644
--- a/gd/packet/parser/fields/checksum_field.h
+++ b/gd/packet/parser/fields/checksum_field.h
@@ -46,6 +46,8 @@
virtual void GenValidator(std::ostream&) const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
private:
std::string type_name_;
};
diff --git a/gd/packet/parser/fields/checksum_start_field.cc b/gd/packet/parser/fields/checksum_start_field.cc
index 5531c5a..3f87db2 100644
--- a/gd/packet/parser/fields/checksum_start_field.cc
+++ b/gd/packet/parser/fields/checksum_start_field.cc
@@ -61,3 +61,7 @@
std::string ChecksumStartField::GetStartedFieldName() const {
return started_field_name_;
}
+
+void ChecksumStartField::GenStringRepresentation(std::ostream&, std::string) const {
+ // Print nothing for checksum start
+}
diff --git a/gd/packet/parser/fields/checksum_start_field.h b/gd/packet/parser/fields/checksum_start_field.h
index c63806b..8c61a82 100644
--- a/gd/packet/parser/fields/checksum_start_field.h
+++ b/gd/packet/parser/fields/checksum_start_field.h
@@ -51,6 +51,8 @@
virtual std::string GetStartedFieldName() const;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
private:
std::string started_field_name_;
};
diff --git a/gd/packet/parser/fields/custom_field.cc b/gd/packet/parser/fields/custom_field.cc
index 4e387f8..fd39714 100644
--- a/gd/packet/parser/fields/custom_field.cc
+++ b/gd/packet/parser/fields/custom_field.cc
@@ -91,3 +91,7 @@
void CustomField::GenValidator(std::ostream&) const {
// Do nothing.
}
+
+void CustomField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << accessor << "->ToString()";
+}
diff --git a/gd/packet/parser/fields/custom_field.h b/gd/packet/parser/fields/custom_field.h
index 621a3c8..7ba1b0f 100644
--- a/gd/packet/parser/fields/custom_field.h
+++ b/gd/packet/parser/fields/custom_field.h
@@ -49,6 +49,8 @@
virtual void GenValidator(std::ostream&) const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
private:
std::string type_name_;
};
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.cc b/gd/packet/parser/fields/custom_field_fixed_size.cc
index 687d48d..54463cd 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.cc
+++ b/gd/packet/parser/fields/custom_field_fixed_size.cc
@@ -62,3 +62,8 @@
void CustomFieldFixedSize::GenValidator(std::ostream&) const {
// Do nothing.
}
+
+void CustomFieldFixedSize::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ // We assume that custom fields will have a ToString() method
+ s << accessor << ".ToString()";
+}
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.h b/gd/packet/parser/fields/custom_field_fixed_size.h
index 97acff9..c53f0b0 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.h
+++ b/gd/packet/parser/fields/custom_field_fixed_size.h
@@ -41,5 +41,7 @@
virtual void GenValidator(std::ostream&) const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
std::string type_name_;
};
diff --git a/gd/packet/parser/fields/enum_field.cc b/gd/packet/parser/fields/enum_field.cc
index 3080d43..8fe56c2 100644
--- a/gd/packet/parser/fields/enum_field.cc
+++ b/gd/packet/parser/fields/enum_field.cc
@@ -51,3 +51,7 @@
void EnumField::GenValidator(std::ostream&) const {
// Do nothing
}
+
+void EnumField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << GetDataType() << "Text(" << accessor << ")";
+}
diff --git a/gd/packet/parser/fields/enum_field.h b/gd/packet/parser/fields/enum_field.h
index 11c64e0..f413553 100644
--- a/gd/packet/parser/fields/enum_field.h
+++ b/gd/packet/parser/fields/enum_field.h
@@ -41,6 +41,8 @@
virtual void GenValidator(std::ostream&) const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
private:
EnumDef enum_def_;
std::string value_;
diff --git a/gd/packet/parser/fields/fixed_scalar_field.cc b/gd/packet/parser/fields/fixed_scalar_field.cc
index 9bfdf0e..e0ad2dd 100644
--- a/gd/packet/parser/fields/fixed_scalar_field.cc
+++ b/gd/packet/parser/fields/fixed_scalar_field.cc
@@ -33,3 +33,7 @@
void FixedScalarField::GenValue(std::ostream& s) const {
s << value_;
}
+
+void FixedScalarField::GenStringRepresentation(std::ostream& s, std::string) const {
+ s << "+" << value_;
+}
diff --git a/gd/packet/parser/fields/fixed_scalar_field.h b/gd/packet/parser/fields/fixed_scalar_field.h
index 0070f6c..0112b27 100644
--- a/gd/packet/parser/fields/fixed_scalar_field.h
+++ b/gd/packet/parser/fields/fixed_scalar_field.h
@@ -33,6 +33,8 @@
virtual std::string GetDataType() const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
static const std::string field_type;
private:
diff --git a/gd/packet/parser/fields/packet_field.cc b/gd/packet/parser/fields/packet_field.cc
index 7b0bdb7..b51999f 100644
--- a/gd/packet/parser/fields/packet_field.cc
+++ b/gd/packet/parser/fields/packet_field.cc
@@ -100,3 +100,7 @@
const PacketField* PacketField::GetElementField() const {
return nullptr;
}
+
+void PacketField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << "\"REPRESENTATION_UNIMPLEMENTED " << GetFieldType() << " " << accessor << "\"";
+}
diff --git a/gd/packet/parser/fields/packet_field.h b/gd/packet/parser/fields/packet_field.h
index d9cc019..55653bd 100644
--- a/gd/packet/parser/fields/packet_field.h
+++ b/gd/packet/parser/fields/packet_field.h
@@ -106,6 +106,9 @@
// Get field of nested elements if this is a container field, nullptr if none
virtual const PacketField* GetElementField() const;
+ // Return string representation of this field, that can be displayed for debugging or logging purposes
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const;
+
std::string GetDebugName() const override;
ParseLocation GetLocation() const override;
diff --git a/gd/packet/parser/fields/payload_field.cc b/gd/packet/parser/fields/payload_field.cc
index c075996..57a1822 100644
--- a/gd/packet/parser/fields/payload_field.cc
+++ b/gd/packet/parser/fields/payload_field.cc
@@ -107,3 +107,8 @@
void PayloadField::GenValidator(std::ostream&) const {
// Do nothing
}
+
+void PayloadField::GenStringRepresentation(std::ostream& s, std::string) const {
+ // TODO: we should parse the child packets
+ s << "\"PAYLOAD[]\"";
+}
diff --git a/gd/packet/parser/fields/payload_field.h b/gd/packet/parser/fields/payload_field.h
index 11e6267..b4ead5b 100644
--- a/gd/packet/parser/fields/payload_field.h
+++ b/gd/packet/parser/fields/payload_field.h
@@ -54,6 +54,8 @@
virtual void GenValidator(std::ostream&) const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
// Payload fields can only be dynamically sized.
const SizeField* size_field_;
// Only used if the size of the payload is based on another field.
diff --git a/gd/packet/parser/fields/scalar_field.cc b/gd/packet/parser/fields/scalar_field.cc
index 320534a..1421273 100644
--- a/gd/packet/parser/fields/scalar_field.cc
+++ b/gd/packet/parser/fields/scalar_field.cc
@@ -129,3 +129,7 @@
void ScalarField::GenValidator(std::ostream&) const {
// Do nothing
}
+
+void ScalarField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << "+" << accessor;
+}
diff --git a/gd/packet/parser/fields/scalar_field.h b/gd/packet/parser/fields/scalar_field.h
index 65f897e..e15d6c1 100644
--- a/gd/packet/parser/fields/scalar_field.h
+++ b/gd/packet/parser/fields/scalar_field.h
@@ -49,6 +49,8 @@
virtual void GenValidator(std::ostream&) const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
private:
const int size_;
};
diff --git a/gd/packet/parser/fields/size_field.cc b/gd/packet/parser/fields/size_field.cc
index 0a1b80b..ce2c899 100644
--- a/gd/packet/parser/fields/size_field.cc
+++ b/gd/packet/parser/fields/size_field.cc
@@ -61,3 +61,7 @@
std::string SizeField::GetSizedFieldName() const {
return sized_field_name_;
}
+
+void SizeField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << accessor;
+}
diff --git a/gd/packet/parser/fields/size_field.h b/gd/packet/parser/fields/size_field.h
index b6e8639..2d40c42 100644
--- a/gd/packet/parser/fields/size_field.h
+++ b/gd/packet/parser/fields/size_field.h
@@ -46,6 +46,8 @@
virtual std::string GetSizedFieldName() const;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
private:
int size_;
std::string sized_field_name_;
diff --git a/gd/packet/parser/fields/struct_field.cc b/gd/packet/parser/fields/struct_field.cc
index fbdafcf..5ada67a 100644
--- a/gd/packet/parser/fields/struct_field.cc
+++ b/gd/packet/parser/fields/struct_field.cc
@@ -83,3 +83,7 @@
void StructField::GenValidator(std::ostream&) const {
// Do nothing
}
+
+void StructField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << accessor << ".ToString()";
+}
diff --git a/gd/packet/parser/fields/struct_field.h b/gd/packet/parser/fields/struct_field.h
index 1f4f100..9d1d463 100644
--- a/gd/packet/parser/fields/struct_field.h
+++ b/gd/packet/parser/fields/struct_field.h
@@ -49,6 +49,8 @@
virtual void GenValidator(std::ostream&) const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
private:
std::string type_name_;
diff --git a/gd/packet/parser/fields/vector_field.cc b/gd/packet/parser/fields/vector_field.cc
index 6990d30..7f37dbe 100644
--- a/gd/packet/parser/fields/vector_field.cc
+++ b/gd/packet/parser/fields/vector_field.cc
@@ -230,3 +230,22 @@
const PacketField* VectorField::GetElementField() const {
return element_field_;
}
+
+void VectorField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+ s << "\"VECTOR[\";";
+
+ std::string arr_idx = "arridx_" + accessor;
+ std::string vec_size = accessor + ".size()";
+ s << "for (size_t index = 0; index < " << vec_size << "; index++) {";
+ std::string element_accessor = "(" + accessor + "[index])";
+ s << "ss << ((index == 0) ? \"\" : \", \") << ";
+
+ if (element_field_->GetFieldType() == CustomField::kFieldType) {
+ s << element_accessor << ".ToString()";
+ } else {
+ element_field_->GenStringRepresentation(s, element_accessor);
+ }
+
+ s << ";}";
+ s << "ss << \"]\"";
+}
diff --git a/gd/packet/parser/fields/vector_field.h b/gd/packet/parser/fields/vector_field.h
index b2ae95d..827f718 100644
--- a/gd/packet/parser/fields/vector_field.h
+++ b/gd/packet/parser/fields/vector_field.h
@@ -67,6 +67,8 @@
virtual const PacketField* GetElementField() const override;
+ virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
const std::string name_;
const PacketField* element_field_{nullptr};
diff --git a/gd/packet/parser/main.cc b/gd/packet/parser/main.cc
index 8b6737a..82deacf 100644
--- a/gd/packet/parser/main.cc
+++ b/gd/packet/parser/main.cc
@@ -121,6 +121,7 @@
out_file << "#pragma once\n";
out_file << "\n\n";
out_file << "#include <stdint.h>\n";
+ out_file << "#include <sstream>\n";
out_file << "#include <string>\n";
out_file << "#include <functional>\n";
out_file << "\n\n";
diff --git a/gd/packet/parser/packet_def.cc b/gd/packet/parser/packet_def.cc
index c37b63b..b2eb199 100644
--- a/gd/packet/parser/packet_def.cc
+++ b/gd/packet/parser/packet_def.cc
@@ -62,6 +62,10 @@
GenValidator(s);
s << "\n";
+ s << " public:";
+ GenParserToString(s);
+ s << "\n";
+
s << " protected:\n";
// Constructor from a View
if (parent_ != nullptr) {
@@ -283,6 +287,35 @@
}
}
+void PacketDef::GenParserToString(std::ostream& s) const {
+ s << "virtual std::string ToString() " << (parent_ != nullptr ? " override" : "") << " {";
+ s << "std::stringstream ss;";
+ s << "ss << std::showbase << std::hex << \"" << name_ << " { \";";
+
+ if (fields_.size() > 0) {
+ s << "ss << \"\" ";
+ bool firstfield = true;
+ for (const auto& field : fields_) {
+ if (field->GetFieldType() == ReservedField::kFieldType || field->GetFieldType() == FixedScalarField::kFieldType ||
+ field->GetFieldType() == ChecksumStartField::kFieldType)
+ continue;
+
+ s << (firstfield ? " << \"" : " << \", ") << field->GetName() << " = \" << ";
+
+ field->GenStringRepresentation(s, field->GetGetterFunctionName() + "()");
+
+ if (firstfield) {
+ firstfield = false;
+ }
+ }
+ s << ";";
+ }
+
+ s << "ss << \" }\";";
+ s << "return ss.str();";
+ s << "}\n";
+}
+
void PacketDef::GenBuilderDefinition(std::ostream& s) const {
s << "class " << name_ << "Builder";
if (parent_ != nullptr) {
diff --git a/gd/packet/parser/packet_def.h b/gd/packet/parser/packet_def.h
index e8acdc3..91be98d 100644
--- a/gd/packet/parser/packet_def.h
+++ b/gd/packet/parser/packet_def.h
@@ -39,6 +39,8 @@
void GenValidator(std::ostream& s) const;
+ void GenParserToString(std::ostream& s) const;
+
TypeDef::Type GetDefinitionType() const;
void GenBuilderDefinition(std::ostream& s) const;
diff --git a/gd/packet/parser/struct_def.cc b/gd/packet/parser/struct_def.cc
index ebd8b45..b174836 100644
--- a/gd/packet/parser/struct_def.cc
+++ b/gd/packet/parser/struct_def.cc
@@ -45,6 +45,37 @@
s << "}";
}
+void StructDef::GenToString(std::ostream& s) const {
+ s << "std::string ToString() {";
+ s << "std::stringstream ss;";
+ s << "ss << std::hex << std::showbase << \"" << name_ << " { \";";
+
+ if (fields_.size() > 0) {
+ s << "ss";
+ bool firstfield = true;
+ for (const auto& field : fields_) {
+ if (field->GetFieldType() == ReservedField::kFieldType ||
+ field->GetFieldType() == ChecksumStartField::kFieldType ||
+ field->GetFieldType() == FixedScalarField::kFieldType || field->GetFieldType() == CountField::kFieldType ||
+ field->GetFieldType() == SizeField::kFieldType)
+ continue;
+
+ s << (firstfield ? " << \"" : " << \", ") << field->GetName() << " = \" << ";
+
+ field->GenStringRepresentation(s, field->GetName() + "_");
+
+ if (firstfield) {
+ firstfield = false;
+ }
+ }
+ s << ";";
+ }
+
+ s << "ss << \" }\";";
+ s << "return ss.str();";
+ s << "}\n";
+}
+
void StructDef::GenParse(std::ostream& s) const {
std::string iterator = (is_little_endian_ ? "Iterator<kLittleEndian>" : "Iterator<!kLittleEndian>");
@@ -81,11 +112,7 @@
if (!fields_.HasBody()) {
s << "size_t end_index = struct_begin_it.NumBytesRemaining();";
- if (parent_ != nullptr) {
- s << "if (end_index < " << GetSize().bytes() << " - to_fill->" << parent_->name_ << "::size())";
- } else {
- s << "if (end_index < " << GetSize().bytes() << ")";
- }
+ s << "if (end_index < " << GetSize().bytes() << ")";
s << "{ return struct_begin_it.Subrange(0,0);}";
}
@@ -127,7 +154,7 @@
s << "}";
}
}
- s << "return struct_begin_it + to_fill->" << name_ << "::size();";
+ s << "return struct_begin_it + to_fill->size();";
s << "}";
}
@@ -175,6 +202,9 @@
GenSpecialize(s);
s << "\n";
+ GenToString(s);
+ s << "\n";
+
GenMembers(s);
for (const auto& field : fields_) {
if (field->GetFieldType() == CountField::kFieldType || field->GetFieldType() == SizeField::kFieldType) {
diff --git a/gd/packet/parser/struct_def.h b/gd/packet/parser/struct_def.h
index 74c1b04..b4c890b 100644
--- a/gd/packet/parser/struct_def.h
+++ b/gd/packet/parser/struct_def.h
@@ -36,6 +36,8 @@
void GenSpecialize(std::ostream& s) const;
+ void GenToString(std::ostream& s) const;
+
void GenParse(std::ostream& s) const;
void GenParseFunctionPrototype(std::ostream& s) const;
diff --git a/gd/packet/parser/test/generated_packet_test.cc b/gd/packet/parser/test/generated_packet_test.cc
index 4feb11e..45b3ec8 100644
--- a/gd/packet/parser/test/generated_packet_test.cc
+++ b/gd/packet/parser/test/generated_packet_test.cc
@@ -1899,6 +1899,68 @@
ASSERT_EQ(1, view.GetAnArray().size());
}
+TEST(GeneratedPacketTest, testToStringOutput) {
+ std::vector<TwoRelatedNumbersBe> count_array;
+ for (uint8_t i = 1; i < 5; i++) {
+ TwoRelatedNumbersBe trn;
+ trn.id_ = i;
+ trn.count_ = 0x0102 * i;
+ count_array.push_back(trn);
+ }
+
+ auto packet = ArrayOfStructBeBuilder::Create(count_array);
+
+ ASSERT_EQ(array_of_struct_be.size(), packet->size());
+
+ std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+ BitInserter it(*packet_bytes);
+ packet->Serialize(it);
+
+ ASSERT_EQ(array_of_struct_be.size(), packet_bytes->size());
+ for (size_t i = 0; i < array_of_struct_be.size(); i++) {
+ ASSERT_EQ(array_of_struct_be[i], packet_bytes->at(i));
+ }
+
+ PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
+ auto view = ArrayOfStructBeView::Create(packet_bytes_view);
+ ASSERT_TRUE(view.IsValid());
+
+ ASSERT_EQ(
+ "ArrayOfStructBe { array_count = 0x4, array = VECTOR[TwoRelatedNumbersBe { id = 0x1, count = 0x102 }, "
+ "TwoRelatedNumbersBe { id = 0x2, count = 0x204 }, TwoRelatedNumbersBe { id = 0x3, count = 0x306 }, "
+ "TwoRelatedNumbersBe { id = 0x4, count = 0x408 }] }",
+ view.ToString());
+}
+
+TEST(GeneratedPacketTest, testToStringOneFixedTypesStruct) {
+ StructWithFixedTypes swf;
+ swf.four_bits_ = FourBits::FIVE;
+ swf.id_ = 0x0d;
+ swf.array_ = {{0x01, 0x02, 0x03}};
+ swf.six_bytes_ = SixBytes{{0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6}};
+
+ auto packet = OneFixedTypesStructBuilder::Create(swf);
+ ASSERT_EQ(one_fixed_types_struct.size(), packet->size());
+
+ std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+ BitInserter it(*packet_bytes);
+ packet->Serialize(it);
+
+ ASSERT_EQ(one_fixed_types_struct.size(), packet_bytes->size());
+ for (size_t i = 0; i < one_fixed_types_struct.size(); i++) {
+ ASSERT_EQ(one_fixed_types_struct[i], packet_bytes->at(i));
+ }
+
+ PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+ auto view = OneFixedTypesStructView::Create(packet_bytes_view);
+ ASSERT_TRUE(view.IsValid());
+
+ ASSERT_EQ(
+ "OneFixedTypesStruct { one = StructWithFixedTypes { four_bits = FIVE, id = 0xd, array = ARRAY[0x1, 0x2, 0x3], "
+ "example_checksum = CHECKSUM, six_bytes = SixBytes } }",
+ view.ToString());
+}
+
} // namespace parser
} // namespace packet
} // namespace bluetooth
diff --git a/gd/packet/parser/test/six_bytes.h b/gd/packet/parser/test/six_bytes.h
index 0f26324..04b0599 100644
--- a/gd/packet/parser/test/six_bytes.h
+++ b/gd/packet/parser/test/six_bytes.h
@@ -53,6 +53,9 @@
bool operator!=(const SixBytes& rhs) const {
return !(*this == rhs);
}
+ std::string ToString() const {
+ return "SixBytes";
+ }
};
} // namespace test
diff --git a/gd/packet/parser/test/variable.h b/gd/packet/parser/test/variable.h
index c452a37..920c14f 100644
--- a/gd/packet/parser/test/variable.h
+++ b/gd/packet/parser/test/variable.h
@@ -62,6 +62,10 @@
*instance = ss.str();
return it;
}
+
+ std::string ToString() const {
+ return data;
+ }
};
} // namespace test
diff --git a/gd/security/cert/simple_security_test.py b/gd/security/cert/simple_security_test.py
index cd4847b..57a1ffa 100644
--- a/gd/security/cert/simple_security_test.py
+++ b/gd/security/cert/simple_security_test.py
@@ -19,7 +19,7 @@
import sys
import logging
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
from cert.event_stream import EventStream
from google.protobuf import empty_pb2 as empty_proto
from facade import common_pb2 as common
@@ -34,7 +34,7 @@
import bluetooth_packets_python3 as bt_packets
-class SimpleSecurityTest(GdFacadeOnlyBaseTestClass):
+class SimpleSecurityTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='SECURITY', cert_module='L2CAP')
diff --git a/gd/security/channel/security_manager_channel.cc b/gd/security/channel/security_manager_channel.cc
index 81bdda1..c07446c 100644
--- a/gd/security/channel/security_manager_channel.cc
+++ b/gd/security/channel/security_manager_channel.cc
@@ -57,6 +57,7 @@
if (fixed_channel_service_ != nullptr) {
fixed_channel_service_->Unregister(common::Bind(&SecurityManagerChannel::OnUnregistered, common::Unretained(this)),
handler_);
+ fixed_channel_service_.reset();
}
}
@@ -132,7 +133,6 @@
result.connection_result_code);
auto entry = fixed_channel_map_.find(address);
if (entry != fixed_channel_map_.end()) {
- entry->second->Release();
entry->second.reset();
fixed_channel_map_.erase(entry);
}
@@ -144,7 +144,6 @@
LOG_ERROR("Connection closed due to: %s", hci::ErrorCodeText(error_code).c_str());
auto entry = fixed_channel_map_.find(address);
if (entry != fixed_channel_map_.end()) {
- entry->second->Release();
entry->second.reset();
fixed_channel_map_.erase(entry);
}
diff --git a/gd/security/channel/security_manager_channel_unittest.cc b/gd/security/channel/security_manager_channel_unittest.cc
index b0fa13e..8e60ba1 100644
--- a/gd/security/channel/security_manager_channel_unittest.cc
+++ b/gd/security/channel/security_manager_channel_unittest.cc
@@ -545,6 +545,13 @@
ASSERT_EQ(OpCode::DELETE_STORED_LINK_KEY, packet_view.GetOpCode());
}
+TEST_F(SecurityManagerChannelTest, recv_encryption_change) {
+ uint16_t connection_handle = 0x0;
+ hci_layer_->IncomingEvent(
+ hci::EncryptionChangeBuilder::Create(hci::ErrorCode::SUCCESS, connection_handle, hci::EncryptionEnabled::ON));
+ ASSERT_TRUE(callback_->receivedEncryptionChange);
+}
+
TEST_F(SecurityManagerChannelTest, recv_encryption_key_refresh) {
uint16_t connection_handle = 0x0;
hci_layer_->IncomingEvent(
diff --git a/gd/security/facade.cc b/gd/security/facade.cc
index a941b40..cfb7445 100644
--- a/gd/security/facade.cc
+++ b/gd/security/facade.cc
@@ -164,6 +164,8 @@
bond_events_.OnIncomingEvent(bonded);
}
+ void OnEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view) override {}
+
void OnDeviceUnbonded(hci::AddressWithType peer) override {
LOG_INFO("%s", peer.ToString().c_str());
BondMsg unbonded;
diff --git a/gd/security/internal/security_manager_impl.cc b/gd/security/internal/security_manager_impl.cc
index a45b376..ebe1ee7 100644
--- a/gd/security/internal/security_manager_impl.cc
+++ b/gd/security/internal/security_manager_impl.cc
@@ -159,6 +159,13 @@
}
}
+void SecurityManagerImpl::NotifyEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view) {
+ for (auto& iter : listeners_) {
+ iter.second->Post(common::Bind(&ISecurityManagerListener::OnEncryptionStateChanged, common::Unretained(iter.first),
+ encryption_change_view));
+ }
+}
+
template <class T>
void SecurityManagerImpl::HandleEvent(T packet) {
ASSERT(packet.IsValid());
@@ -178,7 +185,7 @@
security_database_.FindOrCreate(hci::AddressWithType{bd_addr, hci::AddressType::PUBLIC_DEVICE_ADDRESS});
auto authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
DispatchPairingHandler(record, true, authentication_requirements);
- entry = pairing_handler_map_.find(packet.GetBdAddr());
+ entry = pairing_handler_map_.find(bd_addr);
}
entry->second->OnReceive(packet);
}
@@ -226,15 +233,16 @@
break;
case hci::EventCode::ENCRYPTION_CHANGE: {
- EncryptionChangeView enc_chg_packet = EncryptionChangeView::Create(event);
- if (!enc_chg_packet.IsValid()) {
+ EncryptionChangeView encryption_change_view = EncryptionChangeView::Create(event);
+ if (!encryption_change_view.IsValid()) {
LOG_ERROR("Invalid EncryptionChange packet received");
return;
}
- if (enc_chg_packet.GetConnectionHandle() == pending_le_pairing_.connection_handle_) {
+ if (encryption_change_view.GetConnectionHandle() == pending_le_pairing_.connection_handle_) {
pending_le_pairing_.handler_->OnHciEvent(event);
return;
}
+ NotifyEncryptionStateChanged(encryption_change_view);
break;
}
diff --git a/gd/security/internal/security_manager_impl.h b/gd/security/internal/security_manager_impl.h
index efd69f7..df5af32 100644
--- a/gd/security/internal/security_manager_impl.h
+++ b/gd/security/internal/security_manager_impl.h
@@ -19,6 +19,7 @@
#include <unordered_map>
#include <utility>
+#include "hci/acl_manager.h"
#include "hci/classic_device.h"
#include "l2cap/le/l2cap_le_module.h"
#include "os/handler.h"
@@ -148,6 +149,7 @@
void NotifyDeviceBonded(hci::AddressWithType device);
void NotifyDeviceBondFailed(hci::AddressWithType device, PairingResultOrFailure status);
void NotifyDeviceUnbonded(hci::AddressWithType device);
+ void NotifyEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view);
private:
template <class T>
diff --git a/gd/security/pairing/classic_pairing_handler.cc b/gd/security/pairing/classic_pairing_handler.cc
index 27bdb17..c5ef608 100644
--- a/gd/security/pairing/classic_pairing_handler.cc
+++ b/gd/security/pairing/classic_pairing_handler.cc
@@ -86,6 +86,8 @@
}
void ClassicPairingHandler::Cancel() {
+ if (is_cancelled_) return;
+ is_cancelled_ = true;
PairingResultOrFailure result = PairingResult();
if (last_status_ != hci::ErrorCode::SUCCESS) {
result = PairingFailure(hci::ErrorCodeText(last_status_));
@@ -129,8 +131,6 @@
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
GetRecord()->SetLinkKey(packet.GetLinkKey(), packet.GetKeyType());
- // We are done with the pairing flow
- Cancel();
}
void ClassicPairingHandler::OnReceive(hci::IoCapabilityRequestView packet) {
@@ -162,8 +162,8 @@
if (last_status_ != hci::ErrorCode::SUCCESS) {
LOG_INFO("Failed SimplePairingComplete: %s", hci::ErrorCodeText(last_status_).c_str());
// Cancel here since we won't get LinkKeyNotification
- Cancel();
}
+ Cancel();
}
void ClassicPairingHandler::OnReceive(hci::ReturnLinkKeysView packet) {
diff --git a/gd/security/pairing/classic_pairing_handler.h b/gd/security/pairing/classic_pairing_handler.h
index 518e738..c6493f6 100644
--- a/gd/security/pairing/classic_pairing_handler.h
+++ b/gd/security/pairing/classic_pairing_handler.h
@@ -98,6 +98,7 @@
UI* user_interface_;
os::Handler* user_interface_handler_;
std::string device_name_;
+ bool is_cancelled_ = false;
hci::ErrorCode last_status_ = hci::ErrorCode::UNKNOWN_HCI_COMMAND;
bool locally_initiated_ = false;
diff --git a/gd/security/pairing/classic_pairing_handler_unittest.cc b/gd/security/pairing/classic_pairing_handler_unittest.cc
index c300e5b..efa77f7 100644
--- a/gd/security/pairing/classic_pairing_handler_unittest.cc
+++ b/gd/security/pairing/classic_pairing_handler_unittest.cc
@@ -182,8 +182,8 @@
// <- IoCapabilityResponse
// <- UserConfirmationRequest
// -> UserConfirmationRequestReply (auto)
-// <- SimplePairingComplete
// <- LinkKeyNotification
+// <- SimplePairingComplete
// <- AuthenticationComplete
// -> SetConnectionEncryption
// <- EncryptionChange
diff --git a/gd/security/pairing_handler_le_unittest.cc b/gd/security/pairing_handler_le_unittest.cc
index 1f585fe..94c08ee 100644
--- a/gd/security/pairing_handler_le_unittest.cc
+++ b/gd/security/pairing_handler_le_unittest.cc
@@ -45,18 +45,6 @@
return CommandView::Create(temp_cmd_view);
}
-std::condition_variable outgoing_l2cap_blocker_;
-std::optional<bluetooth::security::CommandView> outgoing_l2cap_packet_;
-
-bool WaitForOutgoingL2capPacket() {
- std::mutex mutex;
- std::unique_lock<std::mutex> lock(mutex);
- if (outgoing_l2cap_blocker_.wait_for(lock, std::chrono::seconds(5)) == std::cv_status::timeout) {
- return false;
- }
- return true;
-}
-
class PairingResultHandlerMock {
public:
MOCK_CONST_METHOD1(OnPairingFinished, void(PairingResultOrFailure));
@@ -111,11 +99,34 @@
outgoing_l2cap_blocker_.notify_one();
}
+ std::optional<bluetooth::security::CommandView> WaitForOutgoingL2capPacket() {
+ std::mutex mutex;
+ std::unique_lock<std::mutex> lock(mutex);
+
+ // It is possible that we lost wakeup from condition_variable, check if data is already waiting to be processed
+ if (outgoing_l2cap_packet_ != std::nullopt) {
+ std::optional<bluetooth::security::CommandView> tmp = std::nullopt;
+ outgoing_l2cap_packet_.swap(tmp);
+ return tmp;
+ }
+
+ // Data not ready yet, wait for it.
+ if (outgoing_l2cap_blocker_.wait_for(lock, std::chrono::seconds(5)) == std::cv_status::timeout) {
+ return std::nullopt;
+ }
+
+ std::optional<bluetooth::security::CommandView> tmp = std::nullopt;
+ outgoing_l2cap_packet_.swap(tmp);
+ return tmp;
+ }
+
public:
os::Thread* thread_;
os::Handler* handler_;
std::unique_ptr<common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>> bidi_queue_;
std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> up_buffer_;
+ std::condition_variable outgoing_l2cap_blocker_;
+ std::optional<bluetooth::security::CommandView> outgoing_l2cap_packet_ = std::nullopt;
};
InitialInformations initial_informations{
@@ -144,8 +155,9 @@
std::unique_ptr<PairingHandlerLe> pairing_handler =
std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
- EXPECT_TRUE(WaitForOutgoingL2capPacket());
- EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_REQUEST);
+ std::optional<bluetooth::security::CommandView> pairing_request = WaitForOutgoingL2capPacket();
+ EXPECT_TRUE(pairing_request.has_value());
+ EXPECT_EQ(pairing_request->GetCode(), Code::PAIRING_REQUEST);
EXPECT_CALL(*pairingResult, OnPairingFinished(VariantWith<PairingFailure>(_))).Times(1);
@@ -154,8 +166,9 @@
bad_pairing_response.IsValid();
pairing_handler->OnCommandView(bad_pairing_response);
- EXPECT_TRUE(WaitForOutgoingL2capPacket());
- EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_FAILED);
+ std::optional<bluetooth::security::CommandView> pairing_failure = WaitForOutgoingL2capPacket();
+ EXPECT_TRUE(pairing_failure.has_value());
+ EXPECT_EQ(pairing_failure->GetCode(), Code::PAIRING_FAILED);
}
TEST_F(PairingHandlerUnitTest, test_secure_connections_just_works) {
@@ -168,9 +181,10 @@
std::unique_ptr<PairingHandlerLe> pairing_handler =
std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
- EXPECT_TRUE(WaitForOutgoingL2capPacket());
- EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_REQUEST);
- CommandView pairing_request = outgoing_l2cap_packet_.value();
+ std::optional<bluetooth::security::CommandView> pairing_request_pkt = WaitForOutgoingL2capPacket();
+ EXPECT_TRUE(pairing_request_pkt.has_value());
+ EXPECT_EQ(pairing_request_pkt->GetCode(), Code::PAIRING_REQUEST);
+ CommandView pairing_request = pairing_request_pkt.value();
auto pairing_response = BuilderToView(
PairingResponseBuilder::Create(IoCapability::KEYBOARD_DISPLAY, OobDataFlag::NOT_PRESENT,
@@ -179,10 +193,11 @@
// Phase 1 finished.
// pairing public key
- EXPECT_TRUE(WaitForOutgoingL2capPacket());
- EXPECT_EQ(Code::PAIRING_PUBLIC_KEY, outgoing_l2cap_packet_->GetCode());
+ std::optional<bluetooth::security::CommandView> public_key_pkt = WaitForOutgoingL2capPacket();
+ EXPECT_TRUE(public_key_pkt.has_value());
+ EXPECT_EQ(Code::PAIRING_PUBLIC_KEY, public_key_pkt->GetCode());
EcdhPublicKey my_public_key;
- auto ppkv = PairingPublicKeyView::Create(outgoing_l2cap_packet_.value());
+ auto ppkv = PairingPublicKeyView::Create(public_key_pkt.value());
ppkv.IsValid();
my_public_key.x = ppkv.GetPublicKeyX();
my_public_key.y = ppkv.GetPublicKeyY();
@@ -205,9 +220,10 @@
pairing_handler->OnCommandView(BuilderToView(PairingConfirmBuilder::Create(Cb)));
// random
- EXPECT_TRUE(WaitForOutgoingL2capPacket());
- EXPECT_EQ(Code::PAIRING_RANDOM, outgoing_l2cap_packet_->GetCode());
- auto prv = PairingRandomView::Create(outgoing_l2cap_packet_.value());
+ std::optional<bluetooth::security::CommandView> random_pkt = WaitForOutgoingL2capPacket();
+ EXPECT_TRUE(random_pkt.has_value());
+ EXPECT_EQ(Code::PAIRING_RANDOM, random_pkt->GetCode());
+ auto prv = PairingRandomView::Create(random_pkt.value());
prv.IsValid();
Octet16 Na = prv.GetRandomValue();
@@ -237,9 +253,10 @@
Octet16 Ea = crypto_toolbox::f6(mac_key, Na, Nb, rb, iocapA.data(), a, b);
Octet16 Eb = crypto_toolbox::f6(mac_key, Nb, Na, ra, iocapB.data(), b, a);
- EXPECT_TRUE(WaitForOutgoingL2capPacket());
- EXPECT_EQ(Code::PAIRING_DH_KEY_CHECK, outgoing_l2cap_packet_->GetCode());
- auto pdhkcv = PairingDhKeyCheckView::Create(outgoing_l2cap_packet_.value());
+ std::optional<bluetooth::security::CommandView> dh_key_pkt = WaitForOutgoingL2capPacket();
+ EXPECT_TRUE(dh_key_pkt.has_value());
+ EXPECT_EQ(Code::PAIRING_DH_KEY_CHECK, dh_key_pkt->GetCode());
+ auto pdhkcv = PairingDhKeyCheckView::Create(dh_key_pkt.value());
pdhkcv.IsValid();
EXPECT_EQ(pdhkcv.GetDhKeyCheck(), Ea);
@@ -280,8 +297,9 @@
// Simulate user accepting the pairing in UI
pairing_handler->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
- EXPECT_TRUE(WaitForOutgoingL2capPacket());
- EXPECT_EQ(Code::PAIRING_REQUEST, outgoing_l2cap_packet_->GetCode());
+ std::optional<bluetooth::security::CommandView> pairing_request_pkt = WaitForOutgoingL2capPacket();
+ EXPECT_TRUE(pairing_request_pkt.has_value());
+ EXPECT_EQ(Code::PAIRING_REQUEST, pairing_request_pkt->GetCode());
// We don't care for the rest of the flow, let it die.
pairing_handler.reset();
@@ -322,8 +340,9 @@
// Simulate user accepting the pairing in UI
pairing_handler->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
- EXPECT_TRUE(WaitForOutgoingL2capPacket());
- EXPECT_EQ(Code::PAIRING_RESPONSE, outgoing_l2cap_packet_->GetCode());
+ std::optional<bluetooth::security::CommandView> pairing_response_pkt = WaitForOutgoingL2capPacket();
+ EXPECT_TRUE(pairing_response_pkt.has_value());
+ EXPECT_EQ(Code::PAIRING_RESPONSE, pairing_response_pkt->GetCode());
// Phase 1 finished.
// We don't care for the rest of the flow, it's handled in in other tests. let it die.
diff --git a/gd/security/security_manager_listener.h b/gd/security/security_manager_listener.h
index edc4e6b..9cad4aa 100644
--- a/gd/security/security_manager_listener.h
+++ b/gd/security/security_manager_listener.h
@@ -19,6 +19,7 @@
#pragma once
#include "common/callback.h"
+#include "hci/acl_manager.h"
namespace bluetooth {
namespace security {
@@ -50,6 +51,13 @@
* @param address of the device that failed to bond
*/
virtual void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) = 0;
+
+ /**
+ * Called as a result of a failure during the bonding process.
+ *
+ * @param address of the device that failed to bond
+ */
+ virtual void OnEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view) = 0;
};
} // namespace security
diff --git a/gd/security/security_module.cc b/gd/security/security_module.cc
index 426aea4..caf9dee 100644
--- a/gd/security/security_module.cc
+++ b/gd/security/security_module.cc
@@ -21,6 +21,7 @@
#include "os/handler.h"
#include "os/log.h"
+#include "hci/acl_manager.h"
#include "hci/hci_layer.h"
#include "l2cap/le/l2cap_le_module.h"
#include "security/channel/security_manager_channel.h"
@@ -34,7 +35,7 @@
struct SecurityModule::impl {
impl(os::Handler* security_handler, l2cap::le::L2capLeModule* l2cap_le_module,
- l2cap::classic::L2capClassicModule* l2cap_classic_module, hci::HciLayer* hci_layer)
+ l2cap::classic::L2capClassicModule* l2cap_classic_module, hci::HciLayer* hci_layer, hci::AclManager* acl_manager)
: security_handler_(security_handler), l2cap_le_module_(l2cap_le_module),
security_manager_channel_(new channel::SecurityManagerChannel(security_handler_, hci_layer,
l2cap_classic_module->GetFixedChannelManager())),
@@ -44,6 +45,7 @@
l2cap::le::L2capLeModule* l2cap_le_module_;
channel::SecurityManagerChannel* security_manager_channel_;
hci::HciLayer* hci_layer_;
+
internal::SecurityManagerImpl security_manager_impl{security_handler_, l2cap_le_module_, security_manager_channel_,
hci_layer_};
~impl() {
@@ -55,11 +57,15 @@
list->add<l2cap::le::L2capLeModule>();
list->add<l2cap::classic::L2capClassicModule>();
list->add<hci::HciLayer>();
+ list->add<hci::AclManager>();
}
void SecurityModule::Start() {
pimpl_ = std::make_unique<impl>(GetHandler(), GetDependency<l2cap::le::L2capLeModule>(),
- GetDependency<l2cap::classic::L2capClassicModule>(), GetDependency<hci::HciLayer>());
+ GetDependency<l2cap::classic::L2capClassicModule>(), GetDependency<hci::HciLayer>(),
+ GetDependency<hci::AclManager>());
+
+ GetDependency<hci::AclManager>()->SetSecurityModule(this);
}
void SecurityModule::Stop() {
diff --git a/gd/setup.py b/gd/setup.py
new file mode 100644
index 0000000..f6b5ba3
--- /dev/null
+++ b/gd/setup.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+from distutils import log
+import os
+from setuptools import setup, find_packages
+from setuptools.command.install import install
+from setuptools.command.develop import develop
+import stat
+import subprocess
+import sys
+
+install_requires = [
+ 'grpcio',
+]
+
+host_executables = [
+ 'root-canal',
+ 'bluetooth_stack_with_facade',
+]
+
+
+def setup_acts_for_cmd_or_die(cmd_str):
+ acts_framework_dir = os.path.abspath('acts_framework')
+ acts_setup_bin = os.path.join(acts_framework_dir, 'setup.py')
+ cmd = [sys.executable, acts_setup_bin, cmd_str]
+ subprocess.check_call(cmd, cwd=acts_framework_dir)
+
+
+def set_permssions_for_host_executables(outputs):
+ for file in outputs:
+ if os.path.basename(file) in host_executables:
+ current_mode = os.stat(file).st_mode
+ new_mode = current_mode | stat.S_IEXEC
+ os.chmod(file, new_mode)
+ log.log(
+ log.INFO, "Changed file mode of %s from %s to %s" %
+ (file, oct(current_mode), oct(new_mode)))
+
+
+class InstallLocalPackagesForInstallation(install):
+
+ def run(self):
+ self.announce('Installing ACTS for installation', log.INFO)
+ setup_acts_for_cmd_or_die("install")
+ self.announce('ACTS installed for installation.', log.INFO)
+ install.run(self)
+ set_permssions_for_host_executables(self.get_outputs())
+
+
+class InstallLocalPackagesForDevelopment(develop):
+
+ def run(self):
+ log.log(log.INFO, 'Installing ACTS for development')
+ setup_acts_for_cmd_or_die("develop")
+ log.log(log.INFO, 'ACTS installed for development')
+ develop.run(self)
+ set_permssions_for_host_executables(self.get_outputs())
+
+
+def main():
+ # Relative path from calling directory to this file
+ our_dir = os.path.dirname(__file__)
+ # Must cd into this dir for package resolution to work
+ # This won't affect the calling shell
+ os.chdir(our_dir)
+ setup(
+ name='bluetooth_cert_tests',
+ version='1.0',
+ author='Android Open Source Project',
+ license='Apache2.0',
+ description="""Bluetooth Cert Tests Package""",
+ # Include root package so that bluetooth_packets_python3.so can be
+ # included as well
+ packages=[''] + find_packages(exclude='acts_framework'),
+ install_requires=install_requires,
+ package_data={
+ '': host_executables + ['*.so', 'lib64/*.so', 'target/*'],
+ 'cert': ['all_test_cases'],
+ },
+ cmdclass={
+ 'install': InstallLocalPackagesForInstallation,
+ 'develop': InstallLocalPackagesForDevelopment,
+ })
+
+
+if __name__ == '__main__':
+ main()
diff --git a/gd/shim/cert/stack_test.py b/gd/shim/cert/stack_test.py
index 2c9315a..3daecc0 100644
--- a/gd/shim/cert/stack_test.py
+++ b/gd/shim/cert/stack_test.py
@@ -17,10 +17,10 @@
import os
import sys
-from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.gd_base_test import GdBaseTestClass
-class StackTest(GdFacadeOnlyBaseTestClass):
+class StackTest(GdBaseTestClass):
def setup_class(self):
super().setup_class(dut_module='SHIM', cert_module='SHIM')
diff --git a/gd/shim/l2cap.cc b/gd/shim/l2cap.cc
index 60ec5c8..106945a 100644
--- a/gd/shim/l2cap.cc
+++ b/gd/shim/l2cap.cc
@@ -81,7 +81,7 @@
ConnectionInterface(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::classic::DynamicChannel> channel,
os::Handler* handler, ConnectionClosed on_closed)
: cid_(cid), channel_(std::move(channel)), handler_(handler), on_data_ready_callback_(nullptr),
- on_connection_closed_callback_(nullptr), address_(channel_->GetDevice()), on_closed_(on_closed) {
+ on_connection_closed_callback_(nullptr), address_(channel_->GetDevice().GetAddress()), on_closed_(on_closed) {
channel_->RegisterOnCloseCallback(
handler_, common::BindOnce(&ConnectionInterface::OnConnectionClosed, common::Unretained(this)));
channel_->GetQueueUpEnd()->RegisterDequeue(
@@ -315,7 +315,8 @@
void OnConnectionOpen(std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
LOG_DEBUG("Local initiated connection is open to device:%s for psm:%hd", address_.ToString().c_str(), psm_);
- ASSERT_LOG(address_ == channel->GetDevice(), " Expected remote device does not match actual remote device");
+ ASSERT_LOG(address_ == channel->GetDevice().GetAddress(),
+ " Expected remote device does not match actual remote device");
pending_open_(std::move(channel));
}
diff --git a/include/hardware/bluetooth.h b/include/hardware/bluetooth.h
index 063abf9..1e3a391 100644
--- a/include/hardware/bluetooth.h
+++ b/include/hardware/bluetooth.h
@@ -625,6 +625,14 @@
* @return a string of uint8_t that is unique to this MAC address
*/
std::string (*obfuscate_address)(const RawAddress& address);
+
+ /**
+ * Get an incremental id for as primary key for Bluetooth metric and log
+ *
+ * @param address Bluetooth MAC address of Bluetooth device
+ * @return int incremental Bluetooth id
+ */
+ int (*get_metric_id)(const RawAddress& address);
} bt_interface_t;
#define BLUETOOTH_INTERFACE_STRING "bluetoothInterface"
diff --git a/main/shim/btif_dm.cc b/main/shim/btif_dm.cc
index e112f36..adf128c 100644
--- a/main/shim/btif_dm.cc
+++ b/main/shim/btif_dm.cc
@@ -45,26 +45,33 @@
std::string name, uint32_t numeric_value) {
bt_bdname_t legacy_name{0};
memcpy(legacy_name.name, name.data(), name.length());
- callback_(RawAddress(address.GetAddress().address), legacy_name, ((0x1F) << 8) /* COD_UNCLASSIFIED*/ , BT_SSP_VARIANT_PASSKEY_CONFIRMATION, numeric_value);
+ callback_(ToRawAddress(address.GetAddress()), legacy_name,
+ ((0x1F) << 8) /* COD_UNCLASSIFIED*/,
+ BT_SSP_VARIANT_PASSKEY_CONFIRMATION, numeric_value);
}
void DisplayYesNoDialog(const bluetooth::hci::AddressWithType& address,
std::string name) {
bt_bdname_t legacy_name{0};
memcpy(legacy_name.name, name.data(), name.length());
- callback_(RawAddress(address.GetAddress().address), legacy_name, ((0x1F) << 8) /* COD_UNCLASSIFIED*/ , BT_SSP_VARIANT_CONSENT, 0);
+ callback_(ToRawAddress(address.GetAddress()), legacy_name,
+ ((0x1F) << 8) /* COD_UNCLASSIFIED*/, BT_SSP_VARIANT_CONSENT, 0);
}
void DisplayEnterPasskeyDialog(const bluetooth::hci::AddressWithType& address, std::string name) {
bt_bdname_t legacy_name{0};
memcpy(legacy_name.name, name.data(), name.length());
- callback_(RawAddress(address.GetAddress().address), legacy_name, ((0x1F) << 8) /* COD_UNCLASSIFIED*/ , BT_SSP_VARIANT_PASSKEY_ENTRY, 0);
+ callback_(ToRawAddress(address.GetAddress()), legacy_name,
+ ((0x1F) << 8) /* COD_UNCLASSIFIED*/, BT_SSP_VARIANT_PASSKEY_ENTRY,
+ 0);
}
void DisplayPasskey(const bluetooth::hci::AddressWithType& address, std::string name, uint32_t passkey) {
bt_bdname_t legacy_name{0};
memcpy(legacy_name.name, name.data(), name.length());
- callback_(RawAddress(address.GetAddress().address), legacy_name, ((0x1F) << 8) /* COD_UNCLASSIFIED*/ , BT_SSP_VARIANT_PASSKEY_NOTIFICATION, passkey);
+ callback_(ToRawAddress(address.GetAddress()), legacy_name,
+ ((0x1F) << 8) /* COD_UNCLASSIFIED*/,
+ BT_SSP_VARIANT_PASSKEY_NOTIFICATION, passkey);
}
void SetLegacyCallback(std::function<void(RawAddress, bt_bdname_t, uint32_t, bt_ssp_variant_t, uint32_t)> callback) {
@@ -98,18 +105,22 @@
bond_state_bonded_cb_ = bond_state_bonded_cb;
bond_state_none_cb_ = bond_state_none_cb;
}
- void OnDeviceBonded(bluetooth::hci::AddressWithType device) {
+
+ void OnDeviceBonded(bluetooth::hci::AddressWithType device) override {
bond_state_bonded_cb_(RawAddress(device.GetAddress().address));
}
- void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) {
+ void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) override {
bond_state_none_cb_(RawAddress(device.GetAddress().address));
}
- void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) {
+ void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) override {
bond_state_none_cb_(RawAddress(device.GetAddress().address));
}
+ void OnEncryptionStateChanged(
+ EncryptionChangeView encryption_change_view) override {}
+
std::function<void(RawAddress)> bond_state_bonding_cb_;
std::function<void(RawAddress)> bond_state_bonded_cb_;
std::function<void(RawAddress)> bond_state_none_cb_;
diff --git a/main/shim/btm.cc b/main/shim/btm.cc
index 6ed636b..8eb884d 100644
--- a/main/shim/btm.cc
+++ b/main/shim/btm.cc
@@ -107,7 +107,7 @@
bluetooth::hci::InquiryResultView view) {
for (auto& response : view.GetInquiryResults()) {
btm_api_process_inquiry_result(
- RawAddress(response.bd_addr_.address),
+ ToRawAddress(response.bd_addr_),
static_cast<uint8_t>(response.page_scan_repetition_mode_),
response.class_of_device_.cod, response.clock_offset_);
}
@@ -117,7 +117,7 @@
bluetooth::hci::InquiryResultWithRssiView view) {
for (auto& response : view.GetInquiryResults()) {
btm_api_process_inquiry_result_with_rssi(
- RawAddress(response.address_.address),
+ ToRawAddress(response.address_),
static_cast<uint8_t>(response.page_scan_repetition_mode_),
response.class_of_device_.cod, response.clock_offset_, response.rssi_);
}
@@ -144,7 +144,7 @@
}
btm_api_process_extended_inquiry_result(
- RawAddress(view.GetAddress().address),
+ ToRawAddress(view.GetAddress()),
static_cast<uint8_t>(view.GetPageScanRepetitionMode()),
view.GetClassOfDevice().cod, view.GetClockOffset(), view.GetRssi(), data,
data_len);
@@ -486,14 +486,14 @@
LOG_DEBUG("%s Start read name from address:%s", __func__,
raw_address.ToString().c_str());
bluetooth::shim::GetName()->ReadRemoteNameRequest(
- hci::Address(raw_address.address), hci::PageScanRepetitionMode::R1,
+ ToGdAddress(raw_address), hci::PageScanRepetitionMode::R1,
0 /* clock_offset */, hci::ClockOffsetValid::INVALID,
base::Bind(
[](tBTM_CMPL_CB* callback, ReadRemoteName* classic_read_remote_name_,
hci::ErrorCode status, hci::Address address,
std::array<uint8_t, kRemoteDeviceNameLength> remote_name) {
- RawAddress raw_address(address.address);
+ RawAddress raw_address = ToRawAddress(address);
BtmRemoteDeviceName name{
.status = (static_cast<uint8_t>(status) == 0)
@@ -719,7 +719,7 @@
return;
}
- RawAddress raw_address(le_report->address_.address);
+ RawAddress raw_address = ToRawAddress(le_report->address_);
btm_ble_process_adv_addr(raw_address, &address_type);
btm_ble_process_adv_pkt_cont(
@@ -746,7 +746,7 @@
.legacy = false,
.continuing = !extended_le_report->complete_,
.truncated = extended_le_report->truncated_});
- RawAddress raw_address(le_report->address_.address);
+ RawAddress raw_address = ToRawAddress(le_report->address_);
if (address_type != BLE_ADDR_ANONYMOUS) {
btm_ble_process_adv_addr(raw_address, &address_type);
}
@@ -794,8 +794,7 @@
bluetooth::shim::GetSecurityModule()->GetSecurityManager();
switch (transport) {
case BT_TRANSPORT_BR_EDR:
- security_manager->CreateBond(
- ToAddressWithType(bd_addr.address, BLE_ADDR_PUBLIC));
+ security_manager->CreateBond(ToAddressWithType(bd_addr, BLE_ADDR_PUBLIC));
break;
case BT_TRANSPORT_LE:
security_manager->CreateBondLe(ToAddressWithType(bd_addr, addr_type));
diff --git a/main/shim/helpers.h b/main/shim/helpers.h
index e6fb3b7..a2fa33f 100644
--- a/main/shim/helpers.h
+++ b/main/shim/helpers.h
@@ -21,10 +21,31 @@
namespace bluetooth {
+inline RawAddress ToRawAddress(const hci::Address& address) {
+ RawAddress ret;
+ ret.address[0] = address.address[5];
+ ret.address[1] = address.address[4];
+ ret.address[2] = address.address[3];
+ ret.address[3] = address.address[2];
+ ret.address[4] = address.address[1];
+ ret.address[5] = address.address[0];
+ return ret;
+}
+
+inline hci::Address ToGdAddress(const RawAddress& address) {
+ hci::Address ret;
+ ret.address[0] = address.address[5];
+ ret.address[1] = address.address[4];
+ ret.address[2] = address.address[3];
+ ret.address[3] = address.address[2];
+ ret.address[4] = address.address[1];
+ ret.address[5] = address.address[0];
+ return ret;
+}
+
inline hci::AddressWithType ToAddressWithType(const RawAddress& legacy_address,
tBLE_ADDR_TYPE legacy_type) {
- // Address and RawAddress are binary equivalent;
- hci::Address address(legacy_address.address);
+ hci::Address address = ToGdAddress(legacy_address);
hci::AddressType type;
if (legacy_type == BLE_ADDR_PUBLIC)
@@ -43,4 +64,4 @@
return hci::AddressWithType{address, type};
}
-} // namespace bluetooth
\ No newline at end of file
+} // namespace bluetooth
diff --git a/profile/avrcp/connection_handler.cc b/profile/avrcp/connection_handler.cc
index 7b2765a..af8fb57 100644
--- a/profile/avrcp/connection_handler.cc
+++ b/profile/avrcp/connection_handler.cc
@@ -132,7 +132,7 @@
return;
};
- return SdpLookup(bdaddr, base::Bind(connection_lambda, this, bdaddr));
+ return SdpLookup(bdaddr, base::Bind(connection_lambda, this, bdaddr), false);
}
bool ConnectionHandler::DisconnectDevice(const RawAddress& bdaddr) {
@@ -155,7 +155,8 @@
return list;
}
-bool ConnectionHandler::SdpLookup(const RawAddress& bdaddr, SdpCallback cb) {
+bool ConnectionHandler::SdpLookup(const RawAddress& bdaddr, SdpCallback cb,
+ bool retry) {
LOG(INFO) << __PRETTY_FUNCTION__;
tAVRC_SDP_DB_PARAMS db_params;
@@ -172,11 +173,11 @@
db_params.p_db = disc_db;
db_params.p_attrs = attr_list;
- return avrc_->FindService(
- UUID_SERVCLASS_AV_REMOTE_CONTROL, bdaddr, &db_params,
- base::Bind(&ConnectionHandler::SdpCb,
- weak_ptr_factory_.GetWeakPtr(), bdaddr, cb, disc_db)) ==
- AVRC_SUCCESS;
+ return avrc_->FindService(UUID_SERVCLASS_AV_REMOTE_CONTROL, bdaddr,
+ &db_params,
+ base::Bind(&ConnectionHandler::SdpCb,
+ weak_ptr_factory_.GetWeakPtr(), bdaddr,
+ cb, disc_db, retry)) == AVRC_SUCCESS;
}
bool ConnectionHandler::AvrcpConnect(bool initiator, const RawAddress& bdaddr) {
@@ -342,7 +343,7 @@
}
};
- SdpLookup(*peer_addr, base::Bind(sdp_lambda, this, handle));
+ SdpLookup(*peer_addr, base::Bind(sdp_lambda, this, handle), false);
avrc_->OpenBrowse(handle, AVCT_ACP);
AvrcpConnect(false, RawAddress::kAny);
@@ -406,10 +407,15 @@
}
void ConnectionHandler::SdpCb(const RawAddress& bdaddr, SdpCallback cb,
- tSDP_DISCOVERY_DB* disc_db, uint16_t status) {
+ tSDP_DISCOVERY_DB* disc_db, bool retry,
+ uint16_t status) {
LOG(INFO) << __PRETTY_FUNCTION__ << ": SDP lookup callback received";
- if (status != AVRC_SUCCESS) {
+ if (status == SDP_CONN_FAILED and !retry) {
+ LOG(WARNING) << __PRETTY_FUNCTION__ << ": SDP Failure retry again";
+ SdpLookup(bdaddr, cb, true);
+ return;
+ } else if (status != AVRC_SUCCESS) {
LOG(ERROR) << __PRETTY_FUNCTION__
<< ": SDP Failure: status = " << (unsigned int)status;
cb.Run(status, 0, 0);
diff --git a/profile/avrcp/connection_handler.h b/profile/avrcp/connection_handler.h
index e22cb6a..d5f0a27 100644
--- a/profile/avrcp/connection_handler.h
+++ b/profile/avrcp/connection_handler.h
@@ -135,9 +135,9 @@
using SdpCallback = base::Callback<void(uint16_t status, uint16_t version,
uint16_t features)>;
- virtual bool SdpLookup(const RawAddress& bdaddr, SdpCallback cb);
+ virtual bool SdpLookup(const RawAddress& bdaddr, SdpCallback cb, bool retry);
void SdpCb(const RawAddress& bdaddr, SdpCallback cb,
- tSDP_DISCOVERY_DB* disc_db, uint16_t status);
+ tSDP_DISCOVERY_DB* disc_db, bool retry, uint16_t status);
virtual bool AvrcpConnect(bool initiator, const RawAddress& bdaddr);
diff --git a/service/hal/fake_bluetooth_interface.cc b/service/hal/fake_bluetooth_interface.cc
index f1d03ad..4af8bc6 100644
--- a/service/hal/fake_bluetooth_interface.cc
+++ b/service/hal/fake_bluetooth_interface.cc
@@ -75,6 +75,7 @@
nullptr, /* interop_database_add */
nullptr, /* get_avrcp_service */
nullptr, /* obfuscate_address */
+ nullptr, /* get_metric_id */
};
} // namespace
diff --git a/stack/btm/btm_ble_addr.cc b/stack/btm/btm_ble_addr.cc
index 000066e..678dd76 100644
--- a/stack/btm/btm_ble_addr.cc
+++ b/stack/btm/btm_ble_addr.cc
@@ -72,7 +72,7 @@
p_cb->own_addr_type = BLE_ADDR_RANDOM;
/* start a periodical timer to refresh random addr */
- uint64_t interval_ms = BTM_BLE_PRIVATE_ADDR_INT_MS;
+ uint64_t interval_ms = btm_get_next_private_addrress_interval_ms();
#if (BTM_BLE_CONFORMANCE_TESTING == TRUE)
interval_ms = btm_cb.ble_ctr_cb.rpa_tout * 1000;
#endif
@@ -93,6 +93,14 @@
std::move(cb)));
}
+uint64_t btm_get_next_private_addrress_interval_ms() {
+ /* 7 minutes minimum, 15 minutes maximum for random address refreshing */
+ const uint64_t interval_min_ms = (7 * 60 * 1000);
+ const uint64_t interval_random_part_max_ms = (8 * 60 * 1000);
+
+ return interval_min_ms + std::rand() % interval_random_part_max_ms;
+}
+
/*******************************************************************************
*
* Function btm_gen_non_resolve_paddr_cmpl
diff --git a/stack/btm/btm_ble_gap.cc b/stack/btm/btm_ble_gap.cc
index 64a8b2a..e208c6c 100644
--- a/stack/btm/btm_ble_gap.cc
+++ b/stack/btm/btm_ble_gap.cc
@@ -88,6 +88,14 @@
return items.front().data;
}
+ bool Exist(uint8_t addr_type, const RawAddress& addr) {
+ auto it = Find(addr_type, addr);
+ if (it != items.end()) {
+ return true;
+ }
+ return false;
+ }
+
/* Append |data| for device |addr_type, addr| */
const std::vector<uint8_t>& Append(uint8_t addr_type, const RawAddress& addr,
std::vector<uint8_t> data) {
@@ -113,6 +121,10 @@
}
}
+ void ClearAll() {
+ items.clear();
+ }
+
private:
struct Item {
uint8_t addr_type;
@@ -448,6 +460,7 @@
/* scan is not started */
if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) {
/* allow config of scan type */
+ cache.ClearAll();
p_inq->scan_type = (p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE)
? BTM_BLE_SCAN_MODE_ACTI
: p_inq->scan_type;
@@ -1298,6 +1311,7 @@
}
if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) {
+ cache.ClearAll();
btm_send_hci_set_scan_params(
BTM_BLE_SCAN_MODE_ACTI, BTM_BLE_LOW_LATENCY_SCAN_INT,
BTM_BLE_LOW_LATENCY_SCAN_WIN,
@@ -1944,11 +1958,19 @@
bool is_scannable = ble_evt_type_is_scannable(evt_type);
bool is_scan_resp = ble_evt_type_is_scan_resp(evt_type);
+ bool is_legacy = ble_evt_type_is_legacy(evt_type);
- bool is_start =
- ble_evt_type_is_legacy(evt_type) && is_scannable && !is_scan_resp;
+ // We might receive a legacy scan response without receving a ADV_IND
+ // or ADV_SCAN_IND before. Only parsing the scan response data which
+ // has no ad flag, the device will be set to DUMO mode. The createbond
+ // procedure will use the wrong device mode.
+ // In such case no necessary to report scan response
+ if(is_legacy && is_scan_resp && !cache.Exist(addr_type, bda))
+ return;
- if (ble_evt_type_is_legacy(evt_type))
+ bool is_start = is_legacy && is_scannable && !is_scan_resp;
+
+ if (is_legacy)
AdvertiseDataParser::RemoveTrailingZeros(tmp);
// We might have send scan request to this device before, but didn't get the
@@ -1993,6 +2015,7 @@
update = false;
} else {
/* if yes, skip it */
+ cache.Clear(addr_type, bda);
return; /* assumption: one result per event */
}
}
diff --git a/stack/btm/btm_ble_int.h b/stack/btm/btm_ble_int.h
index fc1bfcf..b2702fe 100644
--- a/stack/btm/btm_ble_int.h
+++ b/stack/btm/btm_ble_int.h
@@ -139,6 +139,7 @@
extern tBTM_SEC_DEV_REC* btm_ble_resolve_random_addr(
const RawAddress& random_bda);
extern void btm_gen_resolve_paddr_low(const RawAddress& address);
+extern uint64_t btm_get_next_private_addrress_interval_ms();
/* privacy function */
#if (BLE_PRIVACY_SPT == TRUE)
diff --git a/stack/btm/btm_ble_int_types.h b/stack/btm/btm_ble_int_types.h
index d2fa554..375c2b3 100644
--- a/stack/btm/btm_ble_int_types.h
+++ b/stack/btm/btm_ble_int_types.h
@@ -119,9 +119,6 @@
#define BTM_BLE_ISVALID_PARAM(x, min, max) \
(((x) >= (min) && (x) <= (max)) || ((x) == BTM_BLE_CONN_PARAM_UNDEF))
-/* 15 minutes minimum for random address refreshing */
-#define BTM_BLE_PRIVATE_ADDR_INT_MS (15 * 60 * 1000)
-
typedef struct {
uint16_t discoverable_mode;
uint16_t connectable_mode;
diff --git a/stack/btm/btm_ble_multi_adv.cc b/stack/btm/btm_ble_multi_adv.cc
index 120b384..6db7594 100644
--- a/stack/btm/btm_ble_multi_adv.cc
+++ b/stack/btm/btm_ble_multi_adv.cc
@@ -24,6 +24,7 @@
#include "ble_advertiser.h"
#include "ble_advertiser_hci_interface.h"
#include "btm_int_types.h"
+#include "stack/btm/btm_ble_int.h"
#include <string.h>
#include <queue>
@@ -257,7 +258,7 @@
p_inst->own_address = bda;
alarm_set_on_mloop(p_inst->adv_raddr_timer,
- BTM_BLE_PRIVATE_ADDR_INT_MS,
+ btm_get_next_private_addrress_interval_ms(),
btm_ble_adv_raddr_timer_timeout, p_inst);
cb.Run(p_inst->inst_id, BTM_BLE_MULTI_ADV_SUCCESS);
},
@@ -429,7 +430,8 @@
c->self->adv_inst[c->inst_id].tx_power = tx_power;
if (c->self->adv_inst[c->inst_id].own_address_type == BLE_ADDR_PUBLIC) {
- c->self->StartAdvertisingSetAfterAddressPart(std::move(c));
+ auto self = c->self;
+ self->StartAdvertisingSetAfterAddressPart(std::move(c));
return;
}
@@ -449,7 +451,8 @@
return;
}
- c->self->StartAdvertisingSetAfterAddressPart(std::move(c));
+ auto self = c->self;
+ self->StartAdvertisingSetAfterAddressPart(std::move(c));
}, base::Passed(&c)));
}, base::Passed(&c)));
}, base::Passed(&c)));
@@ -492,11 +495,11 @@
return;
}
+ auto self = c->self;
if (c->periodic_params.enable) {
- c->self->StartAdvertisingSetPeriodicPart(
- std::move(c));
+ self->StartAdvertisingSetPeriodicPart(std::move(c));
} else {
- c->self->StartAdvertisingSetFinish(std::move(c));
+ self->StartAdvertisingSetFinish(std::move(c));
}
},
base::Passed(&c)));
@@ -550,7 +553,8 @@
return;
}
- c->self->StartAdvertisingSetFinish(std::move(c));
+ auto self = c->self;
+ self->StartAdvertisingSetFinish(std::move(c));
}, base::Passed(&c)));
}, base::Passed(&c)));
@@ -791,8 +795,9 @@
int length = moreThanOnePacket ? ADV_DATA_LEN_MAX : dataSize - offset;
int newOffset = offset + length;
+ auto dataData = data.data();
sender.Run(
- inst_id, operation, length, data.data() + offset,
+ inst_id, operation, length, dataData + offset,
Bind(&BleAdvertisingManagerImpl::DivideAndSendDataRecursively, false,
inst_id, std::move(data), newOffset, std::move(done_cb), sender));
}
diff --git a/stack/btm/btm_devctl.cc b/stack/btm/btm_devctl.cc
index 2b717b9..52b66b4 100644
--- a/stack/btm/btm_devctl.cc
+++ b/stack/btm/btm_devctl.cc
@@ -199,14 +199,17 @@
l2c_link_processs_num_bufs(controller->get_acl_buffer_count_classic());
+ // setup the random number generator
+ std::srand(std::time(nullptr));
+
#if (BLE_PRIVACY_SPT == TRUE)
/* Set up the BLE privacy settings */
if (controller->supports_ble() && controller->supports_ble_privacy() &&
controller->get_ble_resolving_list_max_size() > 0) {
btm_ble_resolving_list_init(controller->get_ble_resolving_list_max_size());
/* set the default random private address timeout */
- btsnd_hcic_ble_set_rand_priv_addr_timeout(BTM_BLE_PRIVATE_ADDR_INT_MS /
- 1000);
+ btsnd_hcic_ble_set_rand_priv_addr_timeout(
+ btm_get_next_private_addrress_interval_ms() / 1000);
}
#endif
diff --git a/stack/btm/btm_inq.cc b/stack/btm/btm_inq.cc
index 80cf186..e00f7ae 100644
--- a/stack/btm/btm_inq.cc
+++ b/stack/btm/btm_inq.cc
@@ -842,10 +842,9 @@
/* Make sure there is not already one in progress */
if (p_inq->remname_active) {
if (BTM_UseLeLink(p_inq->remname_bda)) {
- if (btm_ble_cancel_remote_name(p_inq->remname_bda))
- return (BTM_CMD_STARTED);
- else
- return (BTM_UNKNOWN_ADDR);
+ /* Cancel remote name request for LE device, and process remote name
+ * callback. */
+ btm_inq_rmt_name_failed_cancelled();
} else
btsnd_hcic_rmt_name_req_cancel(p_inq->remname_bda);
return (BTM_CMD_STARTED);
@@ -1984,22 +1983,22 @@
}
void btm_inq_remote_name_timer_timeout(UNUSED_ATTR void* data) {
- btm_inq_rmt_name_failed();
+ btm_inq_rmt_name_failed_cancelled();
}
/*******************************************************************************
*
- * Function btm_inq_rmt_name_failed
+ * Function btm_inq_rmt_name_failed_cancelled
*
- * Description This function is if timeout expires while getting remote
- * name. This is done for devices that incorrectly do not
- * report operation failure
+ * Description This function is if timeout expires or request is cancelled
+ * while getting remote name. This is done for devices that
+ * incorrectly do not report operation failure
*
* Returns void
*
******************************************************************************/
-void btm_inq_rmt_name_failed(void) {
- BTM_TRACE_ERROR("btm_inq_rmt_name_failed() remname_active=%d",
+void btm_inq_rmt_name_failed_cancelled(void) {
+ BTM_TRACE_ERROR("btm_inq_rmt_name_failed_cancelled() remname_active=%d",
btm_cb.btm_inq_vars.remname_active);
if (btm_cb.btm_inq_vars.remname_active)
diff --git a/stack/btm/btm_int.h b/stack/btm/btm_int.h
index 88cb724..05180db 100644
--- a/stack/btm/btm_int.h
+++ b/stack/btm/btm_int.h
@@ -59,7 +59,7 @@
extern void btm_process_remote_name(const RawAddress* bda, BD_NAME name,
uint16_t evt_len, uint8_t hci_status);
-extern void btm_inq_rmt_name_failed(void);
+extern void btm_inq_rmt_name_failed_cancelled(void);
extern void btm_inq_remote_name_timer_timeout(void* data);
/* Inquiry related functions */
diff --git a/stack/smp/smp_keys.cc b/stack/smp/smp_keys.cc
index a4df7952..a372ba4 100644
--- a/stack/smp/smp_keys.cc
+++ b/stack/smp/smp_keys.cc
@@ -987,7 +987,8 @@
link_key_type += BTM_LTK_DERIVED_LKEY_OFFSET;
- Octet16 notif_link_key = link_key;
+ Octet16 notif_link_key;
+ std::reverse_copy(link_key.begin(), link_key.end(), notif_link_key.begin());
btm_sec_link_key_notification(bda_for_lk, notif_link_key, link_key_type);
SMP_TRACE_EVENT("%s is completed", __func__);
diff --git a/stack/test/ble_advertiser_test.cc b/stack/test/ble_advertiser_test.cc
index 952aa41..98142dd 100644
--- a/stack/test/ble_advertiser_test.cc
+++ b/stack/test/ble_advertiser_test.cc
@@ -68,6 +68,8 @@
void alarm_free(alarm_t* alarm) {}
const controller_t* controller_get_interface() { return nullptr; }
+uint64_t btm_get_next_private_addrress_interval_ms() { return 15 * 60 * 1000; }
+
namespace {
void DoNothing(uint8_t) {}
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
index e9feca4..561ddbb 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
@@ -439,7 +439,7 @@
void DualModeController::ReadRemoteVersionInformation(
CommandPacketView command) {
auto command_view = gd_hci::ReadRemoteVersionInformationView::Create(
- gd_hci::DiscoveryCommandView::Create(command));
+ gd_hci::ConnectionManagementCommandView::Create(command));
ASSERT(command_view.IsValid());
auto status = link_layer_controller_.SendCommandToRemoteByHandle(
@@ -515,7 +515,7 @@
void DualModeController::ReadRemoteExtendedFeatures(CommandPacketView command) {
auto command_view = gd_hci::ReadRemoteExtendedFeaturesView::Create(
- gd_hci::DiscoveryCommandView::Create(command));
+ gd_hci::ConnectionManagementCommandView::Create(command));
ASSERT(command_view.IsValid());
auto status = link_layer_controller_.SendCommandToRemoteByHandle(
@@ -543,7 +543,7 @@
void DualModeController::ReadRemoteSupportedFeatures(
CommandPacketView command) {
auto command_view = gd_hci::ReadRemoteSupportedFeaturesView::Create(
- gd_hci::DiscoveryCommandView::Create(command));
+ gd_hci::ConnectionManagementCommandView::Create(command));
ASSERT(command_view.IsValid());
auto status = link_layer_controller_.SendCommandToRemoteByHandle(