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_ = {0x05 /* Length */, 0x08 /* TYPE_NAME_SHORT */, 'g', 'b', 'e', 'a'};
79 
80   INFO("Scripted_beacon registered {}", registered_);
81 
82   if (args.size() >= 4) {
83     config_file_ = args[2];
84     events_file_ = args[3];
85     set_state(PlaybackEvent::INITIALIZED);
86   } else {
87     ERROR("Initialization failed, need playback and playback events file "
88           "arguments");
89   }
90 }
91 
has_time_elapsed(steady_clock::time_point time_point)92 bool has_time_elapsed(steady_clock::time_point time_point) {
93   return steady_clock::now() > time_point;
94 }
95 
populate_event(PlaybackEvent * event,PlaybackEvent::PlaybackEventType type)96 static void populate_event(PlaybackEvent* event, PlaybackEvent::PlaybackEventType type) {
97   INFO("Adding event: {}", PlaybackEvent::PlaybackEventType_Name(type));
98   event->set_type(type);
99   event->set_secs_since_epoch(system_clock::now().time_since_epoch().count());
100 }
101 
102 // Adds events to events file; we won't be able to post anything to the file
103 // until we set to permissive mode in tests. No events are posted until then.
set_state(PlaybackEvent::PlaybackEventType state)104 void ScriptedBeacon::set_state(PlaybackEvent::PlaybackEventType state) {
105   PlaybackEvent event;
106   current_state_ = state;
107   if (!events_ostream_.is_open()) {
108     events_ostream_.open(events_file_, std::ios::out | std::ios::binary | std::ios::trunc);
109     if (!events_ostream_.is_open()) {
110       INFO("Events file not opened yet, for event: {}",
111            PlaybackEvent::PlaybackEventType_Name(state));
112       return;
113     }
114   }
115   populate_event(&event, state);
116   event.SerializeToOstream(&events_ostream_);
117   events_ostream_.flush();
118 }
119 
Tick()120 void ScriptedBeacon::Tick() {
121   switch (current_state_) {
122     case PlaybackEvent::INITIALIZED:
123       Beacon::Tick();
124       break;
125     case PlaybackEvent::SCANNED_ONCE:
126       next_check_time_ = steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
127       set_state(PlaybackEvent::WAITING_FOR_FILE);
128       break;
129     case PlaybackEvent::WAITING_FOR_FILE:
130       if (!has_time_elapsed(next_check_time_)) {
131         return;
132       }
133       next_check_time_ = steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
134       if (access(config_file_.c_str(), F_OK) == -1) {
135         return;
136       }
137       set_state(PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE);
138       break;
139     case PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE:
140       if (access(config_file_.c_str(), R_OK) == -1) {
141         return;
142       }
143       set_state(PlaybackEvent::PARSING_FILE);
144       break;
145     case PlaybackEvent::PARSING_FILE: {
146       if (!has_time_elapsed(next_check_time_)) {
147         return;
148       }
149       std::fstream input(config_file_, std::ios::in | std::ios::binary);
150       if (!ble_ad_list_.ParseFromIstream(&input)) {
151         ERROR("Cannot parse playback file {}", config_file_);
152         set_state(PlaybackEvent::FILE_PARSING_FAILED);
153         return;
154       }
155       set_state(PlaybackEvent::PLAYBACK_STARTED);
156       INFO("Starting Ble advertisement playback from file: {}", config_file_);
157       next_ad_.ad_time = steady_clock::now();
158       get_next_advertisement();
159       input.close();
160       break;
161     }
162     case PlaybackEvent::PLAYBACK_STARTED: {
163       while (has_time_elapsed(next_ad_.ad_time)) {
164         auto ad = model::packets::LeLegacyAdvertisingPduBuilder::Create(
165                 next_ad_.address, Address::kEmpty /* Destination */, AddressType::RANDOM,
166                 AddressType::PUBLIC, LegacyAdvertisingType::ADV_NONCONN_IND, next_ad_.ad);
167         SendLinkLayerPacket(std::move(ad), Phy::Type::LOW_ENERGY);
168         if (packet_num_ < ble_ad_list_.advertisements().size()) {
169           get_next_advertisement();
170         } else {
171           set_state(PlaybackEvent::PLAYBACK_ENDED);
172           if (events_ostream_.is_open()) {
173             events_ostream_.close();
174           }
175           INFO("Completed Ble advertisement playback from file: {} with {} "
176                "packets",
177                config_file_, packet_num_);
178           break;
179         }
180       }
181     } break;
182     case PlaybackEvent::FILE_PARSING_FAILED:
183     case PlaybackEvent::PLAYBACK_ENDED:
184     case PlaybackEvent::UNKNOWN:
185       return;
186   }
187 }
188 
ReceiveLinkLayerPacket(model::packets::LinkLayerPacketView packet,Phy::Type,int8_t)189 void ScriptedBeacon::ReceiveLinkLayerPacket(model::packets::LinkLayerPacketView packet,
190                                             Phy::Type /*type*/, int8_t /*rssi*/) {
191   if (current_state_ == PlaybackEvent::INITIALIZED) {
192     if (packet.GetDestinationAddress() == address_ && packet.GetType() == PacketType::LE_SCAN) {
193       set_state(PlaybackEvent::SCANNED_ONCE);
194       SendLinkLayerPacket(
195               model::packets::LeScanResponseBuilder::Create(
196                       address_, packet.GetSourceAddress(), AddressType::PUBLIC,
197                       std::vector(scan_response_data_.begin(), scan_response_data_.end())),
198               Phy::Type::LOW_ENERGY);
199     }
200   }
201 }
202 
get_next_advertisement()203 void ScriptedBeacon::get_next_advertisement() {
204   std::string payload = ble_ad_list_.advertisements(packet_num_).payload();
205   std::string mac_address = ble_ad_list_.advertisements(packet_num_).mac_address();
206   uint32_t delay_before_send_ms = ble_ad_list_.advertisements(packet_num_).delay_before_send_ms();
207   next_ad_.ad.assign(payload.begin(), payload.end());
208   if (Address::IsValidAddress(mac_address)) {
209     // formatted string with colons like "12:34:56:78:9a:bc"
210     Address::FromString(mac_address, next_ad_.address);
211   } else if (mac_address.size() == Address::kLength) {
212     // six-byte binary address
213     std::vector<uint8_t> mac_vector(mac_address.cbegin(), mac_address.cend());
214     next_ad_.address.Address::FromOctets(mac_vector.data());
215   } else {
216     Address::FromString("BA:D0:AD:BA:D0:AD", next_ad_.address);
217   }
218   next_ad_.ad_time += steady_clock::duration(std::chrono::milliseconds(delay_before_send_ms));
219   packet_num_++;
220 }
221 }  // namespace rootcanal
222