[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) {
[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<BluetoothDeathRecipient>& death_recipient) {
if (death_recipient->getHasDied())
LOG_INFO("Skipping unlink call, service died.");
#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_ =
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_) {
- 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<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) {
- 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<model::packets::LinkLayerPacketBuilder> 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(
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);
}
}
}
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<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