Loading system/test/rootcanal/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ cc_binary { "libhidlbase", "liblog", "libutils", "libprotobuf-cpp-lite", ], cflags: [ "-fvisibility=hidden", Loading @@ -49,6 +50,7 @@ cc_binary { "android.hardware.bluetooth-hci", "libbt-rootcanal", "libbt-rootcanal-types", "libscriptedbeaconpayload-protos-lite", ], include_dirs: [ "packages/modules/Bluetooth/system", Loading Loading @@ -78,6 +80,7 @@ cc_library_shared { "libhidlbase", "liblog", "libutils", "libprotobuf-cpp-lite", ], cflags: [ "-Wall", Loading @@ -94,6 +97,7 @@ cc_library_shared { "android.hardware.bluetooth-hci", "libbt-rootcanal", "libbt-rootcanal-types", "libscriptedbeaconpayload-protos-lite", ], include_dirs: [ "packages/modules/Bluetooth/system", Loading system/test/rootcanal/bluetooth_hci.cc +5 −2 Original line number Diff line number Diff line Loading @@ -18,13 +18,13 @@ #include "bluetooth_hci.h" #include "log/log.h" #include <cutils/properties.h> #include <netdb.h> #include <netinet/in.h> #include <string.h> #include "hci_internals.h" #include "os/log.h" namespace android { namespace hardware { Loading Loading @@ -85,7 +85,6 @@ Return<void> BluetoothHci::initialize_impl( const sp<V1_0::IBluetoothHciCallbacks>& cb, const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) { LOG_INFO("%s", __func__); if (cb == nullptr) { LOG_ERROR("cb == nullptr! -> Unable to call initializationComplete(ERR)"); return Void(); Loading Loading @@ -190,6 +189,10 @@ Return<void> BluetoothHci::initialize_impl( 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()) Loading system/vendor_libs/test_vendor_lib/Android.bp +15 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ cc_library_static { "model/devices/loopback.cc", "model/devices/polled_socket.cc", "model/devices/remote_loopback_device.cc", "model/devices/scripted_beacon.cc", "model/devices/sniffer.cc", "model/setup/async_manager.cc", "model/setup/device_boutique.cc", Loading Loading @@ -66,9 +67,21 @@ cc_library_static { ], static_libs: [ "libbt-rootcanal-types", "libscriptedbeaconpayload-protos-lite", ], } cc_library_static { name: "libscriptedbeaconpayload-protos-lite", host_supported: true, proprietary: true, proto: { export_proto_headers: true, type: "lite", }, srcs: ["model/devices/scripted_beacon_ble_payload.proto"], } // test-vendor unit tests for host // ======================================================== cc_test_host { Loading Loading @@ -136,6 +149,8 @@ cc_binary_host { ], static_libs: [ "libbt-rootcanal-types", "libprotobuf-cpp-lite", "libscriptedbeaconpayload-protos-lite", "libbt-rootcanal", ], sanitize: { Loading system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc 0 → 100644 +216 −0 Original line number Diff line number Diff line /* * Copyright 2016 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. */ #define OS_ANDROID #include "os/log.h" #undef OS_ANDROID #include "scripted_beacon.h" #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" using std::vector; namespace test_vendor_lib { bool ScriptedBeacon::registered_ = DeviceBoutique::Register("scripted_beacon", &ScriptedBeacon::Create); ScriptedBeacon::ScriptedBeacon() { advertising_interval_ms_ = std::chrono::milliseconds(1280); properties_.SetLeAdvertisementType(0x02 /* SCANNABLE */); properties_.SetLeAdvertisement({ 0x18, // Length 0x09 /* TYPE_NAME_CMPL */, 'g', 'D', 'e', 'v', 'i', 'c', 'e', '-', 's', 'c', 'r', 'i', 'p', 't', 'e', 'd', '-', 'b', 'e', 'a', 'c', 'o', 'n', 0x02, // Length 0x01 /* TYPE_FLAG */, 0x4 /* BREDR_NOT_SPT */ | 0x2 /* GEN_DISC_FLAG */, }); properties_.SetLeScanResponse({0x05, // Length 0x08, // TYPE_NAME_SHORT 'g', 'b', 'e', 'a'}); } 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; } } void ScriptedBeacon::Initialize(const vector<std::string>& args) { if (args.size() < 2) return; Address addr{}; if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr); if (args.size() < 3) return; config_file_ = args[2]; } 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)) { return; } if (!is_config_file_ready()) { next_check_time = std::chrono::steady_clock::now() + std::chrono::steady_clock::duration(std::chrono::seconds(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; } } 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()); return; } 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); } get_next_advertisement(); } last_timer_tick_ = now; } } void ScriptedBeacon::IncomingPacket( model::packets::LinkLayerPacketView packet) { if (!scanned_once_) { if (packet.GetDestinationAddress() == properties_.GetLeAddress() && packet.GetType() == model::packets::PacketType::LE_SCAN) { auto scan_response = model::packets::LeScanResponseBuilder::Create( properties_.GetLeAddress(), packet.GetSourceAddress(), static_cast<model::packets::AddressType>( properties_.GetLeAddressType()), model::packets::AdvertisementType::SCAN_RESPONSE, 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]) { 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()); 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 { play_back_complete_ = true; LOG_INFO("%s: Completed Ble advertisement playback from file: %s", __func__, config_file_.c_str()); } } } // namespace test_vendor_lib system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * 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 <cstdint> #include <vector> #include "model/devices/scripted_beacon_ble_payload.pb.h" #include "beacon.h" namespace test_vendor_lib { // Pretend to be a lot of beacons by advertising from a file. class ScriptedBeacon : public Beacon { public: ScriptedBeacon(); virtual ~ScriptedBeacon() = default; static std::shared_ptr<Device> Create() { return std::make_shared<ScriptedBeacon>(); } // Return a string representation of the type of device. virtual std::string GetTypeString() const override { return "scripted_beacon"; } virtual std::string ToString() const override { return "scripted_beacon " + config_file_; } // Set the address and advertising interval from string args. void Initialize(const std::vector<std::string>& args) override; void TimerTick() override; void IncomingPacket(model::packets::LinkLayerPacketView packet_view) override; 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_{}; struct Advertisement { std::vector<uint8_t> ad; Address address; std::chrono::steady_clock::time_point ad_time; }; void get_next_advertisement(); bool is_config_file_ready(); Advertisement next_ad_{}; 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 Loading
system/test/rootcanal/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ cc_binary { "libhidlbase", "liblog", "libutils", "libprotobuf-cpp-lite", ], cflags: [ "-fvisibility=hidden", Loading @@ -49,6 +50,7 @@ cc_binary { "android.hardware.bluetooth-hci", "libbt-rootcanal", "libbt-rootcanal-types", "libscriptedbeaconpayload-protos-lite", ], include_dirs: [ "packages/modules/Bluetooth/system", Loading Loading @@ -78,6 +80,7 @@ cc_library_shared { "libhidlbase", "liblog", "libutils", "libprotobuf-cpp-lite", ], cflags: [ "-Wall", Loading @@ -94,6 +97,7 @@ cc_library_shared { "android.hardware.bluetooth-hci", "libbt-rootcanal", "libbt-rootcanal-types", "libscriptedbeaconpayload-protos-lite", ], include_dirs: [ "packages/modules/Bluetooth/system", Loading
system/test/rootcanal/bluetooth_hci.cc +5 −2 Original line number Diff line number Diff line Loading @@ -18,13 +18,13 @@ #include "bluetooth_hci.h" #include "log/log.h" #include <cutils/properties.h> #include <netdb.h> #include <netinet/in.h> #include <string.h> #include "hci_internals.h" #include "os/log.h" namespace android { namespace hardware { Loading Loading @@ -85,7 +85,6 @@ Return<void> BluetoothHci::initialize_impl( const sp<V1_0::IBluetoothHciCallbacks>& cb, const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) { LOG_INFO("%s", __func__); if (cb == nullptr) { LOG_ERROR("cb == nullptr! -> Unable to call initializationComplete(ERR)"); return Void(); Loading Loading @@ -190,6 +189,10 @@ Return<void> BluetoothHci::initialize_impl( 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()) Loading
system/vendor_libs/test_vendor_lib/Android.bp +15 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ cc_library_static { "model/devices/loopback.cc", "model/devices/polled_socket.cc", "model/devices/remote_loopback_device.cc", "model/devices/scripted_beacon.cc", "model/devices/sniffer.cc", "model/setup/async_manager.cc", "model/setup/device_boutique.cc", Loading Loading @@ -66,9 +67,21 @@ cc_library_static { ], static_libs: [ "libbt-rootcanal-types", "libscriptedbeaconpayload-protos-lite", ], } cc_library_static { name: "libscriptedbeaconpayload-protos-lite", host_supported: true, proprietary: true, proto: { export_proto_headers: true, type: "lite", }, srcs: ["model/devices/scripted_beacon_ble_payload.proto"], } // test-vendor unit tests for host // ======================================================== cc_test_host { Loading Loading @@ -136,6 +149,8 @@ cc_binary_host { ], static_libs: [ "libbt-rootcanal-types", "libprotobuf-cpp-lite", "libscriptedbeaconpayload-protos-lite", "libbt-rootcanal", ], sanitize: { Loading
system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc 0 → 100644 +216 −0 Original line number Diff line number Diff line /* * Copyright 2016 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. */ #define OS_ANDROID #include "os/log.h" #undef OS_ANDROID #include "scripted_beacon.h" #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" using std::vector; namespace test_vendor_lib { bool ScriptedBeacon::registered_ = DeviceBoutique::Register("scripted_beacon", &ScriptedBeacon::Create); ScriptedBeacon::ScriptedBeacon() { advertising_interval_ms_ = std::chrono::milliseconds(1280); properties_.SetLeAdvertisementType(0x02 /* SCANNABLE */); properties_.SetLeAdvertisement({ 0x18, // Length 0x09 /* TYPE_NAME_CMPL */, 'g', 'D', 'e', 'v', 'i', 'c', 'e', '-', 's', 'c', 'r', 'i', 'p', 't', 'e', 'd', '-', 'b', 'e', 'a', 'c', 'o', 'n', 0x02, // Length 0x01 /* TYPE_FLAG */, 0x4 /* BREDR_NOT_SPT */ | 0x2 /* GEN_DISC_FLAG */, }); properties_.SetLeScanResponse({0x05, // Length 0x08, // TYPE_NAME_SHORT 'g', 'b', 'e', 'a'}); } 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; } } void ScriptedBeacon::Initialize(const vector<std::string>& args) { if (args.size() < 2) return; Address addr{}; if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr); if (args.size() < 3) return; config_file_ = args[2]; } 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)) { return; } if (!is_config_file_ready()) { next_check_time = std::chrono::steady_clock::now() + std::chrono::steady_clock::duration(std::chrono::seconds(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; } } 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()); return; } 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); } get_next_advertisement(); } last_timer_tick_ = now; } } void ScriptedBeacon::IncomingPacket( model::packets::LinkLayerPacketView packet) { if (!scanned_once_) { if (packet.GetDestinationAddress() == properties_.GetLeAddress() && packet.GetType() == model::packets::PacketType::LE_SCAN) { auto scan_response = model::packets::LeScanResponseBuilder::Create( properties_.GetLeAddress(), packet.GetSourceAddress(), static_cast<model::packets::AddressType>( properties_.GetLeAddressType()), model::packets::AdvertisementType::SCAN_RESPONSE, 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]) { 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()); 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 { play_back_complete_ = true; LOG_INFO("%s: Completed Ble advertisement playback from file: %s", __func__, config_file_.c_str()); } } } // namespace test_vendor_lib
system/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * 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 <cstdint> #include <vector> #include "model/devices/scripted_beacon_ble_payload.pb.h" #include "beacon.h" namespace test_vendor_lib { // Pretend to be a lot of beacons by advertising from a file. class ScriptedBeacon : public Beacon { public: ScriptedBeacon(); virtual ~ScriptedBeacon() = default; static std::shared_ptr<Device> Create() { return std::make_shared<ScriptedBeacon>(); } // Return a string representation of the type of device. virtual std::string GetTypeString() const override { return "scripted_beacon"; } virtual std::string ToString() const override { return "scripted_beacon " + config_file_; } // Set the address and advertising interval from string args. void Initialize(const std::vector<std::string>& args) override; void TimerTick() override; void IncomingPacket(model::packets::LinkLayerPacketView packet_view) override; 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_{}; struct Advertisement { std::vector<uint8_t> ad; Address address; std::chrono::steady_clock::time_point ad_time; }; void get_next_advertisement(); bool is_config_file_ready(); Advertisement next_ad_{}; 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