1 /*
2  * Copyright 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "model/devices/scripted_beacon.h"
18 
19 #include <unistd.h>
20 
21 #include <cstdint>
22 #include <fstream>
23 
24 #include "log.h"
25 #include "model/devices/scripted_beacon_ble_payload.pb.h"
26 #include "model/setup/device_boutique.h"
27 
28 #ifdef _WIN32
29 #define F_OK 00
30 #define R_OK 04
31 #endif
32 
33 using std::vector;
34 using std::chrono::steady_clock;
35 using std::chrono::system_clock;
36 
37 namespace rootcanal {
38 using namespace model::packets;
39 using namespace std::chrono_literals;
40 
41 bool ScriptedBeacon::registered_ =
42     DeviceBoutique::Register("scripted_beacon", &ScriptedBeacon::Create);
43 
ScriptedBeacon(const vector<std::string> & args)44 ScriptedBeacon::ScriptedBeacon(const vector<std::string>& args) : Beacon(args) {
45   advertising_interval_ = 1280ms;
46   advertising_type_ = LegacyAdvertisingType::ADV_SCAN_IND;
47   advertising_data_ = {
48       0x18 /* Length */,
49       0x09 /* TYPE_NAME_CMPL */,
50       'g',
51       'D',
52       'e',
53       'v',
54       'i',
55       'c',
56       'e',
57       '-',
58       's',
59       'c',
60       'r',
61       'i',
62       'p',
63       't',
64       'e',
65       'd',
66       '-',
67       'b',
68       'e',
69       'a',
70       'c',
71       'o',
72       'n',
73       0x02 /* Length */,
74       0x01 /* TYPE_FLAG */,
75       0x4 /* BREDR_NOT_SPT */ | 0x2 /* GEN_DISC_FLAG */,
76   };
77 
78   scan_response_data_ = {
79       0x05 /* Length */, 0x08 /* TYPE_NAME_SHORT */, 'g', 'b', 'e', 'a'};
80 
81   INFO("Scripted_beacon registered {}", registered_);
82 
83   if (args.size() >= 4) {
84     config_file_ = args[2];
85     events_file_ = args[3];
86     set_state(PlaybackEvent::INITIALIZED);
87   } else {
88     ERROR(
89         "Initialization failed, need playback and playback events file "
90         "arguments");
91   }
92 }
93 
has_time_elapsed(steady_clock::time_point time_point)94 bool has_time_elapsed(steady_clock::time_point time_point) {
95   return steady_clock::now() > time_point;
96 }
97 
populate_event(PlaybackEvent * event,PlaybackEvent::PlaybackEventType type)98 static void populate_event(PlaybackEvent* event,
99                            PlaybackEvent::PlaybackEventType type) {
100   INFO("Adding event: {}", PlaybackEvent::PlaybackEventType_Name(type));
101   event->set_type(type);
102   event->set_secs_since_epoch(system_clock::now().time_since_epoch().count());
103 }
104 
105 // Adds events to events file; we won't be able to post anything to the file
106 // until we set to permissive mode in tests. No events are posted until then.
set_state(PlaybackEvent::PlaybackEventType state)107 void ScriptedBeacon::set_state(PlaybackEvent::PlaybackEventType state) {
108   PlaybackEvent event;
109   current_state_ = state;
110   if (!events_ostream_.is_open()) {
111     events_ostream_.open(events_file_,
112                          std::ios::out | std::ios::binary | std::ios::trunc);
113     if (!events_ostream_.is_open()) {
114       INFO("Events file not opened yet, for event: {}",
115            PlaybackEvent::PlaybackEventType_Name(state));
116       return;
117     }
118   }
119   populate_event(&event, state);
120   event.SerializeToOstream(&events_ostream_);
121   events_ostream_.flush();
122 }
123 
Tick()124 void ScriptedBeacon::Tick() {
125   switch (current_state_) {
126     case PlaybackEvent::INITIALIZED:
127       Beacon::Tick();
128       break;
129     case PlaybackEvent::SCANNED_ONCE:
130       next_check_time_ =
131           steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
132       set_state(PlaybackEvent::WAITING_FOR_FILE);
133       break;
134     case PlaybackEvent::WAITING_FOR_FILE:
135       if (!has_time_elapsed(next_check_time_)) {
136         return;
137       }
138       next_check_time_ =
139           steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
140       if (access(config_file_.c_str(), F_OK) == -1) {
141         return;
142       }
143       set_state(PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE);
144       break;
145     case PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE:
146       if (access(config_file_.c_str(), R_OK) == -1) {
147         return;
148       }
149       set_state(PlaybackEvent::PARSING_FILE);
150       break;
151     case PlaybackEvent::PARSING_FILE: {
152       if (!has_time_elapsed(next_check_time_)) {
153         return;
154       }
155       std::fstream input(config_file_, std::ios::in | std::ios::binary);
156       if (!ble_ad_list_.ParseFromIstream(&input)) {
157         ERROR("Cannot parse playback file {}", config_file_);
158         set_state(PlaybackEvent::FILE_PARSING_FAILED);
159         return;
160       }
161       set_state(PlaybackEvent::PLAYBACK_STARTED);
162       INFO("Starting Ble advertisement playback from file: {}", config_file_);
163       next_ad_.ad_time = steady_clock::now();
164       get_next_advertisement();
165       input.close();
166       break;
167     }
168     case PlaybackEvent::PLAYBACK_STARTED: {
169       while (has_time_elapsed(next_ad_.ad_time)) {
170         auto ad = model::packets::LeLegacyAdvertisingPduBuilder::Create(
171             next_ad_.address, Address::kEmpty /* Destination */,
172             AddressType::RANDOM, AddressType::PUBLIC,
173             LegacyAdvertisingType::ADV_NONCONN_IND, next_ad_.ad);
174         SendLinkLayerPacket(std::move(ad), Phy::Type::LOW_ENERGY);
175         if (packet_num_ < ble_ad_list_.advertisements().size()) {
176           get_next_advertisement();
177         } else {
178           set_state(PlaybackEvent::PLAYBACK_ENDED);
179           if (events_ostream_.is_open()) {
180             events_ostream_.close();
181           }
182           INFO(
183               "Completed Ble advertisement playback from file: {} with {} "
184               "packets",
185               config_file_, packet_num_);
186           break;
187         }
188       }
189     } break;
190     case PlaybackEvent::FILE_PARSING_FAILED:
191     case PlaybackEvent::PLAYBACK_ENDED:
192     case PlaybackEvent::UNKNOWN:
193       return;
194   }
195 }
196 
ReceiveLinkLayerPacket(model::packets::LinkLayerPacketView packet,Phy::Type,int8_t)197 void ScriptedBeacon::ReceiveLinkLayerPacket(
198     model::packets::LinkLayerPacketView packet, Phy::Type /*type*/,
199     int8_t /*rssi*/) {
200   if (current_state_ == PlaybackEvent::INITIALIZED) {
201     if (packet.GetDestinationAddress() == address_ &&
202         packet.GetType() == PacketType::LE_SCAN) {
203       set_state(PlaybackEvent::SCANNED_ONCE);
204       SendLinkLayerPacket(
205           std::move(model::packets::LeScanResponseBuilder::Create(
206               address_, packet.GetSourceAddress(), AddressType::PUBLIC,
207               std::vector(scan_response_data_.begin(),
208                           scan_response_data_.end()))),
209           Phy::Type::LOW_ENERGY);
210     }
211   }
212 }
213 
get_next_advertisement()214 void ScriptedBeacon::get_next_advertisement() {
215   std::string payload = ble_ad_list_.advertisements(packet_num_).payload();
216   std::string mac_address =
217       ble_ad_list_.advertisements(packet_num_).mac_address();
218   uint32_t delay_before_send_ms =
219       ble_ad_list_.advertisements(packet_num_).delay_before_send_ms();
220   next_ad_.ad.assign(payload.begin(), payload.end());
221   if (Address::IsValidAddress(mac_address)) {
222     // formatted string with colons like "12:34:56:78:9a:bc"
223     Address::FromString(mac_address, next_ad_.address);
224   } else if (mac_address.size() == Address::kLength) {
225     // six-byte binary address
226     std::vector<uint8_t> mac_vector(mac_address.cbegin(), mac_address.cend());
227     next_ad_.address.Address::FromOctets(mac_vector.data());
228   } else {
229     Address::FromString("BA:D0:AD:BA:D0:AD", next_ad_.address);
230   }
231   next_ad_.ad_time +=
232       steady_clock::duration(std::chrono::milliseconds(delay_before_send_ms));
233   packet_num_++;
234 }
235 }  // namespace rootcanal
236