Loading system/test/rootcanal/bluetooth_hci.cc +23 −15 Original line number Diff line number Diff line Loading @@ -164,8 +164,17 @@ Return<void> 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) { Loading @@ -178,21 +187,20 @@ Return<void> BluetoothHci::initialize_impl( [this](int fd) { test_model_.IncomingHciConnection(fd); }); SetUpLinkLayerServer( 6311, [this](int fd) { test_model_.IncomingLinkLayerConnection(fd); }); } // Add some default devices for easier debugging test_channel_.AddDefaults(); } 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_channel_.AddDeviceToPhy({"2", "1"}); test_model_.AddDeviceToPhy(controller_index + 1, low_energy_phy_index); test_channel_.Add({"beacon", "be:ac:10:00:00:02", "1000"}); test_channel_.AddDeviceToPhy({"3", "1"}); 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"}); test_channel_.AddDeviceToPhy({"4", "1"}); "/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({}); } unlink_cb_ = [this, cb](sp<BluetoothDeathRecipient>& death_recipient) { if (death_recipient->getHasDied()) Loading system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc +124 −96 Original line number Diff line number Diff line Loading @@ -19,16 +19,14 @@ #include <fstream> #include <cstdint> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/inotify.h> #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_ = Loading Loading @@ -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<std::string>& 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_) { switch (current_state_) { case PlaybackEvent::INITIALIZED: 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)) { 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)) { 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; } LOG_INFO("%s: Starting Ble advertisement playback from file: %s", __func__, config_file_.c_str()); play_back_on_ = true; } 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(); } } break; case PlaybackEvent::PLAYBACK_STARTED: { std::shared_ptr<model::packets::LinkLayerPacketBuilder> 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) { 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 (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) { 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(); } last_timer_tick_ = now; LOG_INFO( "Completed Ble advertisement playback from file: %s with %d " "packets", config_file_.c_str(), packet_num_); break; } } } 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( Loading @@ -183,9 +210,8 @@ void ScriptedBeacon::IncomingPacket( properties_.GetLeScanResponse()); std::shared_ptr<model::packets::LinkLayerPacketBuilder> 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); } } Loading @@ -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(); 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(); 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<uint8_t> 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 system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h +13 −9 Original line number Diff line number Diff line Loading @@ -18,10 +18,13 @@ #include <cstdint> #include <vector> #include <fstream> #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 { Loading Loading @@ -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<uint8_t> 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 system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto +18 −0 Original line number Diff line number Diff line Loading @@ -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; } Loading
system/test/rootcanal/bluetooth_hci.cc +23 −15 Original line number Diff line number Diff line Loading @@ -164,8 +164,17 @@ Return<void> 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) { Loading @@ -178,21 +187,20 @@ Return<void> BluetoothHci::initialize_impl( [this](int fd) { test_model_.IncomingHciConnection(fd); }); SetUpLinkLayerServer( 6311, [this](int fd) { test_model_.IncomingLinkLayerConnection(fd); }); } // Add some default devices for easier debugging test_channel_.AddDefaults(); } 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_channel_.AddDeviceToPhy({"2", "1"}); test_model_.AddDeviceToPhy(controller_index + 1, low_energy_phy_index); test_channel_.Add({"beacon", "be:ac:10:00:00:02", "1000"}); test_channel_.AddDeviceToPhy({"3", "1"}); 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"}); test_channel_.AddDeviceToPhy({"4", "1"}); "/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({}); } unlink_cb_ = [this, cb](sp<BluetoothDeathRecipient>& death_recipient) { if (death_recipient->getHasDied()) Loading
system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc +124 −96 Original line number Diff line number Diff line Loading @@ -19,16 +19,14 @@ #include <fstream> #include <cstdint> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/inotify.h> #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_ = Loading Loading @@ -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<std::string>& 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_) { switch (current_state_) { case PlaybackEvent::INITIALIZED: 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)) { 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)) { 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; } LOG_INFO("%s: Starting Ble advertisement playback from file: %s", __func__, config_file_.c_str()); play_back_on_ = true; } 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(); } } break; case PlaybackEvent::PLAYBACK_STARTED: { std::shared_ptr<model::packets::LinkLayerPacketBuilder> 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) { 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 (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) { 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(); } last_timer_tick_ = now; LOG_INFO( "Completed Ble advertisement playback from file: %s with %d " "packets", config_file_.c_str(), packet_num_); break; } } } 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( Loading @@ -183,9 +210,8 @@ void ScriptedBeacon::IncomingPacket( properties_.GetLeScanResponse()); std::shared_ptr<model::packets::LinkLayerPacketBuilder> 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); } } Loading @@ -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(); 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(); 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<uint8_t> 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
system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h +13 −9 Original line number Diff line number Diff line Loading @@ -18,10 +18,13 @@ #include <cstdint> #include <vector> #include <fstream> #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 { Loading Loading @@ -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<uint8_t> 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
system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto +18 −0 Original line number Diff line number Diff line Loading @@ -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; }