From 80b43139bf68d5324e0b86dc7be8682ed5eadee4 Mon Sep 17 00:00:00 2001 From: Sanjay Ramankandath Date: Wed, 3 Jun 2020 22:24:06 +0000 Subject: [PATCH] RootCanal: Use a state machine in ScriptedBeacon Add indication for ble add playback completion bt HCI: Make sure the device numbers match Clean up static variables in scripted beacon Clean up logs in scripted beacon Test: boot cuttlefish and check logcat Tag: #gd-refactor Bug: 154491371 Bug: 158019041 (cherry picked from commit 79f168b7f3b03722cf2d0bb5ee97258824dc3832) (cherry picked from commit 1c66bdaa38c46e859b66f8cc055993c5f0e7f5a1) (cherry picked from commit 82656e9682e9de7596b7327618cf680936f34589) Change-Id: I7f54ecef4ba08acc33b9d773b02b8428ca91a415 (cherry picked from commit 53c68dba78cc8f746c93e3db062ef358a976f0bf) Merged-In: I7f54ecef4ba08acc33b9d773b02b8428ca91a415 --- test/rootcanal/bluetooth_hci.cc | 38 ++-- .../model/devices/scripted_beacon.cc | 220 ++++++++++++--------- .../model/devices/scripted_beacon.h | 22 ++- .../devices/scripted_beacon_ble_payload.proto | 18 ++ 4 files changed, 178 insertions(+), 120 deletions(-) diff --git a/test/rootcanal/bluetooth_hci.cc b/test/rootcanal/bluetooth_hci.cc index 17ae6e763..908554bce 100644 --- a/test/rootcanal/bluetooth_hci.cc +++ b/test/rootcanal/bluetooth_hci.cc @@ -164,8 +164,17 @@ Return BluetoothHci::initialize_impl( [this](AsyncTaskId task) { async_manager_.CancelAsyncTask(task); }); test_model_.Reset(); + // Add the controller as a device in the model. - test_model_.Add(controller_); + size_t controller_index = test_model_.Add(controller_); + size_t low_energy_phy_index = + test_model_.AddPhy(test_vendor_lib::Phy::Type::LOW_ENERGY); + size_t classic_phy_index = + test_model_.AddPhy(test_vendor_lib::Phy::Type::BR_EDR); + test_model_.AddDeviceToPhy(controller_index, low_energy_phy_index); + test_model_.AddDeviceToPhy(controller_index, classic_phy_index); + test_model_.SetTimerPeriod(std::chrono::milliseconds(10)); + test_model_.StartTimer(); // Send responses to logcat if the test channel is not configured. test_channel_.RegisterSendResponse([](const std::string& response) { @@ -178,22 +187,21 @@ Return BluetoothHci::initialize_impl( [this](int fd) { test_model_.IncomingHciConnection(fd); }); SetUpLinkLayerServer( 6311, [this](int fd) { test_model_.IncomingLinkLayerConnection(fd); }); + } else { + // This should be configurable in the future. + LOG_INFO("Adding Beacons so the scan list is not empty."); + test_channel_.Add({"beacon", "be:ac:10:00:00:01", "1000"}); + test_model_.AddDeviceToPhy(controller_index + 1, low_energy_phy_index); + test_channel_.Add({"beacon", "be:ac:10:00:00:02", "1000"}); + test_model_.AddDeviceToPhy(controller_index + 2, low_energy_phy_index); + test_channel_.Add( + {"scripted_beacon", "5b:ea:c1:00:00:03", + "/data/vendor/bluetooth/bluetooth_sim_ble_playback_file", + "/data/vendor/bluetooth/bluetooth_sim_ble_playback_events"}); + test_model_.AddDeviceToPhy(controller_index + 3, low_energy_phy_index); + test_channel_.List({}); } - // Add some default devices for easier debugging - test_channel_.AddDefaults(); - - // This should be configurable in the future. - LOG_INFO("Adding Beacons so the scan list is not empty."); - test_channel_.Add({"beacon", "be:ac:10:00:00:01", "1000"}); - test_channel_.AddDeviceToPhy({"2", "1"}); - test_channel_.Add({"beacon", "be:ac:10:00:00:02", "1000"}); - test_channel_.AddDeviceToPhy({"3", "1"}); - test_channel_.Add( - {"scripted_beacon", "5b:ea:c1:00:00:03", - "/data/vendor/bluetooth/bluetooth_sim_ble_playback_file"}); - test_channel_.AddDeviceToPhy({"4", "1"}); - unlink_cb_ = [this, cb](sp& death_recipient) { if (death_recipient->getHasDied()) LOG_INFO("Skipping unlink call, service died."); diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc index 79f508c82..ffbdd8c46 100644 --- a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc +++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc @@ -19,16 +19,14 @@ #include #include #include -#include -#include -#include -#include #include "model/devices/scripted_beacon_ble_payload.pb.h" #include "model/setup/device_boutique.h" #include "os/log.h" using std::vector; +using std::chrono::steady_clock; +using std::chrono::system_clock; namespace test_vendor_lib { bool ScriptedBeacon::registered_ = @@ -70,109 +68,138 @@ ScriptedBeacon::ScriptedBeacon() { properties_.SetLeScanResponse({0x05, // Length 0x08, // TYPE_NAME_SHORT 'g', 'b', 'e', 'a'}); + LOG_INFO("Scripted_beacon registered %s", registered_ ? "true" : "false"); } -bool ScriptedBeacon::is_config_file_ready() { - static bool file_absence_logged = false; - if (access(config_file_.c_str(), F_OK) == -1) { - if (!file_absence_logged) { - LOG_INFO("%s: playback file %s not available", - __func__, - config_file_.c_str()); - file_absence_logged = true; - } - return false; - } - - if (access(config_file_.c_str(), R_OK) == -1) { - LOG_ERROR("%s: playback file %s is not readable", - __func__, - config_file_.c_str()); - return false; - } - LOG_INFO("%s: playback file %s is available and readable", - __func__, - config_file_.c_str()); - return true; -} - -bool has_time_elapsed(std::chrono::steady_clock::time_point time_point) { - std::chrono::steady_clock::time_point now = - std::chrono::steady_clock::now(); - if (now > time_point) { - return true; - } else { - return false; - } +bool has_time_elapsed(steady_clock::time_point time_point) { + return steady_clock::now() > time_point; } void ScriptedBeacon::Initialize(const vector& args) { - if (args.size() < 2) return; + if (args.size() < 2) { + LOG_ERROR( + "Initialization failed, need mac address, playback and playback events " + "file arguments"); + return; + } Address addr{}; if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr); - if (args.size() < 3) return; - + if (args.size() < 4) { + LOG_ERROR( + "Initialization failed, need playback and playback events file " + "arguments"); + } config_file_ = args[2]; + events_file_ = args[3]; + set_state(PlaybackEvent::INITIALIZED); +} + +void ScriptedBeacon::populate_event(PlaybackEvent * event, PlaybackEvent::PlaybackEventType type) { + LOG_INFO("Adding event: %d", type); + event->set_type(type); + event->set_secs_since_epoch(system_clock::now().time_since_epoch().count()); +} + +// Adds events to events file; we won't be able to post anything to the file +// until we set to permissive mode in tests. No events are posted until then. +void ScriptedBeacon::set_state(PlaybackEvent::PlaybackEventType state) { + PlaybackEvent event; + current_state_ = state; + if (!events_ostream_.is_open()) { + events_ostream_.open(events_file_, std::ios::out | std::ios::binary | std::ios::trunc); + if (!events_ostream_.is_open()) { + LOG_INFO("Events file not opened yet, for event: %d", state); + return; + } + } + populate_event(&event, state); + event.SerializeToOstream(&events_ostream_); + events_ostream_.flush(); } void ScriptedBeacon::TimerTick() { - if (!scanned_once_) { - Beacon::TimerTick(); - } else { - static std::chrono::steady_clock::time_point next_check_time = - std::chrono::steady_clock::now(); - if (!play_back_on_) { - if (!has_time_elapsed(next_check_time)) { + switch (current_state_) { + case PlaybackEvent::INITIALIZED: + Beacon::TimerTick(); + break; + case PlaybackEvent::SCANNED_ONCE: + next_check_time_ = + steady_clock::now() + steady_clock::duration(std::chrono::seconds(1)); + set_state(PlaybackEvent::WAITING_FOR_FILE); + break; + case PlaybackEvent::WAITING_FOR_FILE: + if (!has_time_elapsed(next_check_time_)) { return; } - if (!is_config_file_ready()) { - next_check_time = std::chrono::steady_clock::now() + - std::chrono::steady_clock::duration(std::chrono::seconds(1)); + next_check_time_ = + steady_clock::now() + steady_clock::duration(std::chrono::seconds(1)); + if (access(config_file_.c_str(), F_OK) == -1) { return; } - // Give time for the file to be written completely before being read - { - static std::chrono::steady_clock::time_point write_delay_next_check_time = - std::chrono::steady_clock::now() + - std::chrono::steady_clock::duration(std::chrono::seconds(1)); - if (!has_time_elapsed(write_delay_next_check_time)) { - return; - } + set_state(PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE); + break; + case PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE: + if (access(config_file_.c_str(), R_OK) == -1) { + return; + } + set_state(PlaybackEvent::PARSING_FILE); + break; + case PlaybackEvent::PARSING_FILE: { + if (!has_time_elapsed(next_check_time_)) { + return; } - std::fstream input(config_file_, std::ios::in | std::ios::binary); if (!ble_ad_list_.ParseFromIstream(&input)) { - LOG_ERROR("%s: Cannot parse playback file %s", __func__, config_file_.c_str()); + LOG_ERROR("Cannot parse playback file %s", config_file_.c_str()); + set_state(PlaybackEvent::FILE_PARSING_FAILED); return; + } else { + set_state(PlaybackEvent::PLAYBACK_STARTED); + LOG_INFO("Starting Ble advertisement playback from file: %s", + config_file_.c_str()); + next_ad_.ad_time = steady_clock::now(); + get_next_advertisement(); + input.close(); } - LOG_INFO("%s: Starting Ble advertisement playback from file: %s", __func__, config_file_.c_str()); - play_back_on_ = true; - get_next_advertisement(); - } - std::shared_ptr to_send; - std::chrono::steady_clock::time_point now = - std::chrono::steady_clock::now(); - elapsed_time_ += now - last_timer_tick_; - while (play_back_on_ && !play_back_complete_ && next_ad_.ad_time < now) { - auto ad = model::packets::LeAdvertisementBuilder::Create( - next_ad_.address, Address::kEmpty /* Destination */, - model::packets::AddressType::RANDOM, - model::packets::AdvertisementType::ADV_NONCONN_IND, next_ad_.ad); - to_send = std::move(ad); - for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) { - phy->Send(to_send); + } break; + case PlaybackEvent::PLAYBACK_STARTED: { + std::shared_ptr to_send; + while (has_time_elapsed(next_ad_.ad_time)) { + auto ad = model::packets::LeAdvertisementBuilder::Create( + next_ad_.address, Address::kEmpty /* Destination */, + model::packets::AddressType::RANDOM, + model::packets::AdvertisementType::ADV_NONCONN_IND, next_ad_.ad); + to_send = std::move(ad); + for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) { + phy->Send(to_send); + } + if (packet_num_ < ble_ad_list_.advertisements().size()) { + get_next_advertisement(); + } else { + set_state(PlaybackEvent::PLAYBACK_ENDED); + if (events_ostream_.is_open()) { + events_ostream_.close(); + } + LOG_INFO( + "Completed Ble advertisement playback from file: %s with %d " + "packets", + config_file_.c_str(), packet_num_); + break; + } } - get_next_advertisement(); - } - last_timer_tick_ = now; + } break; + case PlaybackEvent::FILE_PARSING_FAILED: + case PlaybackEvent::PLAYBACK_ENDED: + case PlaybackEvent::UNKNOWN: + return; } } void ScriptedBeacon::IncomingPacket( model::packets::LinkLayerPacketView packet) { - if (!scanned_once_) { + if (current_state_ == PlaybackEvent::INITIALIZED) { if (packet.GetDestinationAddress() == properties_.GetLeAddress() && packet.GetType() == model::packets::PacketType::LE_SCAN) { auto scan_response = model::packets::LeScanResponseBuilder::Create( @@ -183,9 +210,8 @@ void ScriptedBeacon::IncomingPacket( properties_.GetLeScanResponse()); std::shared_ptr to_send = std::move(scan_response); - scanned_once_ = true; - Address::FromString("12:34:56:78:9A:BC", next_ad_.address); - for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) { + set_state(PlaybackEvent::SCANNED_ONCE); + for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) { phy->Send(to_send); } } @@ -193,22 +219,24 @@ void ScriptedBeacon::IncomingPacket( } void ScriptedBeacon::get_next_advertisement() { - static int packet_num = 0; - - if (packet_num < ble_ad_list_.advertisements().size()) { - std::string payload = ble_ad_list_.advertisements(packet_num).payload(); - std::string mac_address = ble_ad_list_.advertisements(packet_num).mac_address(); - uint32_t delay_before_send_ms = - ble_ad_list_.advertisements(packet_num).delay_before_send_ms(); - next_ad_.ad.assign(payload.begin(), payload.end()); + std::string payload = ble_ad_list_.advertisements(packet_num_).payload(); + std::string mac_address = + ble_ad_list_.advertisements(packet_num_).mac_address(); + uint32_t delay_before_send_ms = + ble_ad_list_.advertisements(packet_num_).delay_before_send_ms(); + next_ad_.ad.assign(payload.begin(), payload.end()); + if (Address::IsValidAddress(mac_address)) { + // formatted string with colons like "12:34:56:78:9a:bc" Address::FromString(mac_address, next_ad_.address); - next_ad_.ad_time = std::chrono::steady_clock::now() + - std::chrono::steady_clock::duration( - std::chrono::milliseconds(delay_before_send_ms)); - packet_num++; + } else if (mac_address.size() == Address::kLength) { + // six-byte binary address + std::vector mac_vector(mac_address.cbegin(), mac_address.cend()); + next_ad_.address.Address::FromOctets(mac_vector.data()); } else { - play_back_complete_ = true; - LOG_INFO("%s: Completed Ble advertisement playback from file: %s", __func__, config_file_.c_str()); + Address::FromString("BA:D0:AD:BA:D0:AD", next_ad_.address); } + next_ad_.ad_time += + steady_clock::duration(std::chrono::milliseconds(delay_before_send_ms)); + packet_num_++; } } // namespace test_vendor_lib diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h index b9a39a525..05406d05e 100644 --- a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h +++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h @@ -18,10 +18,13 @@ #include #include +#include #include "model/devices/scripted_beacon_ble_payload.pb.h" #include "beacon.h" +using android::bluetooth::test_vendor_lib::model::devices::ScriptedBeaconBleAdProto::PlaybackEvent; + namespace test_vendor_lib { // Pretend to be a lot of beacons by advertising from a file. class ScriptedBeacon : public Beacon { @@ -51,26 +54,27 @@ class ScriptedBeacon : public Beacon { private: static bool registered_; - bool scanned_once_{false}; - std::chrono::steady_clock::duration elapsed_time_{}; - std::chrono::steady_clock::time_point last_timer_tick_{}; std::string config_file_{}; + std::string events_file_{}; + std::ofstream events_ostream_; struct Advertisement { std::vector ad; Address address; std::chrono::steady_clock::time_point ad_time; }; + void populate_event(PlaybackEvent * event, PlaybackEvent::PlaybackEventType type); + void get_next_advertisement(); - bool is_config_file_ready(); + void set_state( + android::bluetooth::test_vendor_lib::model::devices:: + ScriptedBeaconBleAdProto::PlaybackEvent::PlaybackEventType type); Advertisement next_ad_{}; - + int packet_num_{0}; + PlaybackEvent::PlaybackEventType current_state_{PlaybackEvent::UNKNOWN}; + std::chrono::steady_clock::time_point next_check_time_{}; android::bluetooth::test_vendor_lib::model::devices::ScriptedBeaconBleAdProto::BleAdvertisementList ble_ad_list_; - - bool play_back_on_{false}; - - bool play_back_complete_{false}; }; } // namespace test_vendor_lib diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto index fd40eb7f0..588519ce4 100644 --- a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto +++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto @@ -13,3 +13,21 @@ message BleAdvertisement { message BleAdvertisementList { repeated BleAdvertisement advertisements = 1; } + +message PlaybackEvent { + // These events should occur in order, starting from INITIALIZED + enum PlaybackEventType { + UNKNOWN = 0; + INITIALIZED = 1; + SCANNED_ONCE = 2; + WAITING_FOR_FILE = 3; + WAITING_FOR_FILE_TO_BE_READABLE = 4; + PARSING_FILE = 5; + PLAYBACK_STARTED = 6; + PLAYBACK_ENDED = 7; + // Error conditions + FILE_PARSING_FAILED = 8; + } + optional PlaybackEventType type = 1; + optional uint64 secs_since_epoch = 2; +} -- 2.11.0