OSDN Git Service

RootCanal: Use a state machine in ScriptedBeacon
authorSanjay Ramankandath <sanjayram@google.com>
Wed, 3 Jun 2020 22:24:06 +0000 (22:24 +0000)
committerMyles Watson <mylesgw@google.com>
Tue, 16 Jun 2020 20:00:41 +0000 (13:00 -0700)
  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
vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc
vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h
vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto

index 17ae6e7..908554b 100644 (file)
@@ -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) {
@@ -178,22 +187,21 @@ Return<void> 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<BluetoothDeathRecipient>& death_recipient) {
     if (death_recipient->getHasDied())
       LOG_INFO("Skipping unlink call, service died.");
index 79f508c..ffbdd8c 100644 (file)
 #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_ =
@@ -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_) {
-    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(
@@ -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);
       }
     }
@@ -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<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
index b9a39a5..05406d0 100644 (file)
 
 #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 {
@@ -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
index fd40eb7..588519c 100644 (file)
@@ -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;
+}