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 "scripted_beacon.h"
18 
19 #include <fstream>
20 #include <cstdint>
21 #include <unistd.h>
22 
23 #include "model/devices/scripted_beacon_ble_payload.pb.h"
24 #include "model/setup/device_boutique.h"
25 #include "os/log.h"
26 
27 using std::vector;
28 using std::chrono::steady_clock;
29 using std::chrono::system_clock;
30 
31 namespace test_vendor_lib {
32 bool ScriptedBeacon::registered_ =
33     DeviceBoutique::Register("scripted_beacon", &ScriptedBeacon::Create);
ScriptedBeacon()34 ScriptedBeacon::ScriptedBeacon() {
35   advertising_interval_ms_ = std::chrono::milliseconds(1280);
36   properties_.SetLeAdvertisementType(0x02 /* SCANNABLE */);
37   properties_.SetLeAdvertisement({
38       0x18,  // Length
39       0x09 /* TYPE_NAME_CMPL */,
40       'g',
41       'D',
42       'e',
43       'v',
44       'i',
45       'c',
46       'e',
47       '-',
48       's',
49       'c',
50       'r',
51       'i',
52       'p',
53       't',
54       'e',
55       'd',
56       '-',
57       'b',
58       'e',
59       'a',
60       'c',
61       'o',
62       'n',
63       0x02,  // Length
64       0x01 /* TYPE_FLAG */,
65       0x4 /* BREDR_NOT_SPT */ | 0x2 /* GEN_DISC_FLAG */,
66   });
67 
68   properties_.SetLeScanResponse({0x05,  // Length
69                                  0x08,  // TYPE_NAME_SHORT
70                                  'g', 'b', 'e', 'a'});
71   LOG_INFO("Scripted_beacon registered %s", registered_ ? "true" : "false");
72 }
73 
has_time_elapsed(steady_clock::time_point time_point)74 bool has_time_elapsed(steady_clock::time_point time_point) {
75   return steady_clock::now() > time_point;
76 }
77 
Initialize(const vector<std::string> & args)78 void ScriptedBeacon::Initialize(const vector<std::string>& args) {
79   if (args.size() < 2) {
80     LOG_ERROR(
81         "Initialization failed, need mac address, playback and playback events "
82         "file arguments");
83     return;
84   }
85 
86   Address addr{};
87   if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr);
88 
89   if (args.size() < 4) {
90     LOG_ERROR(
91         "Initialization failed, need playback and playback events file "
92         "arguments");
93   }
94   config_file_ = args[2];
95   events_file_ = args[3];
96   set_state(PlaybackEvent::INITIALIZED);
97 }
98 
populate_event(PlaybackEvent * event,PlaybackEvent::PlaybackEventType type)99 void ScriptedBeacon::populate_event(PlaybackEvent * event, PlaybackEvent::PlaybackEventType type) {
100   LOG_INFO("Adding event: %d", 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_, std::ios::out | std::ios::binary | std::ios::trunc);
112     if (!events_ostream_.is_open()) {
113       LOG_INFO("Events file not opened yet, for event: %d", state);
114       return;
115     }
116   }
117   populate_event(&event, state);
118   event.SerializeToOstream(&events_ostream_);
119   events_ostream_.flush();
120 }
121 
TimerTick()122 void ScriptedBeacon::TimerTick() {
123   switch (current_state_) {
124     case PlaybackEvent::INITIALIZED:
125       Beacon::TimerTick();
126       break;
127     case PlaybackEvent::SCANNED_ONCE:
128       next_check_time_ =
129           steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
130       set_state(PlaybackEvent::WAITING_FOR_FILE);
131       break;
132     case PlaybackEvent::WAITING_FOR_FILE:
133       if (!has_time_elapsed(next_check_time_)) {
134         return;
135       }
136       next_check_time_ =
137           steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
138       if (access(config_file_.c_str(), F_OK) == -1) {
139         return;
140       }
141       set_state(PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE);
142       break;
143     case PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE:
144       if (access(config_file_.c_str(), R_OK) == -1) {
145         return;
146       }
147       set_state(PlaybackEvent::PARSING_FILE);
148       break;
149     case PlaybackEvent::PARSING_FILE: {
150       if (!has_time_elapsed(next_check_time_)) {
151         return;
152       }
153       std::fstream input(config_file_, std::ios::in | std::ios::binary);
154       if (!ble_ad_list_.ParseFromIstream(&input)) {
155         LOG_ERROR("Cannot parse playback file %s", config_file_.c_str());
156         set_state(PlaybackEvent::FILE_PARSING_FAILED);
157         return;
158       } else {
159         set_state(PlaybackEvent::PLAYBACK_STARTED);
160         LOG_INFO("Starting Ble advertisement playback from file: %s",
161                  config_file_.c_str());
162         next_ad_.ad_time = steady_clock::now();
163         get_next_advertisement();
164         input.close();
165       }
166     } break;
167     case PlaybackEvent::PLAYBACK_STARTED: {
168       while (has_time_elapsed(next_ad_.ad_time)) {
169         auto ad = model::packets::LeAdvertisementBuilder::Create(
170             next_ad_.address, Address::kEmpty /* Destination */,
171             model::packets::AddressType::RANDOM,
172             model::packets::AdvertisementType::ADV_NONCONN_IND, next_ad_.ad);
173         SendLinkLayerPacket(std::move(ad), Phy::Type::LOW_ENERGY);
174         if (packet_num_ < ble_ad_list_.advertisements().size()) {
175           get_next_advertisement();
176         } else {
177           set_state(PlaybackEvent::PLAYBACK_ENDED);
178           if (events_ostream_.is_open()) {
179             events_ostream_.close();
180           }
181           LOG_INFO(
182               "Completed Ble advertisement playback from file: %s with %d "
183               "packets",
184               config_file_.c_str(), packet_num_);
185           break;
186         }
187       }
188     } break;
189     case PlaybackEvent::FILE_PARSING_FAILED:
190     case PlaybackEvent::PLAYBACK_ENDED:
191     case PlaybackEvent::UNKNOWN:
192       return;
193   }
194 }
195 
IncomingPacket(model::packets::LinkLayerPacketView packet)196 void ScriptedBeacon::IncomingPacket(
197     model::packets::LinkLayerPacketView packet) {
198   if (current_state_ == PlaybackEvent::INITIALIZED) {
199     if (packet.GetDestinationAddress() == properties_.GetLeAddress() &&
200         packet.GetType() == model::packets::PacketType::LE_SCAN) {
201       auto scan_response = model::packets::LeScanResponseBuilder::Create(
202           properties_.GetLeAddress(), packet.GetSourceAddress(),
203           static_cast<model::packets::AddressType>(
204               properties_.GetLeAddressType()),
205           model::packets::AdvertisementType::SCAN_RESPONSE,
206           properties_.GetLeScanResponse());
207       set_state(PlaybackEvent::SCANNED_ONCE);
208       SendLinkLayerPacket(std::move(scan_response), Phy::Type::LOW_ENERGY);
209     }
210   }
211 }
212 
get_next_advertisement()213 void ScriptedBeacon::get_next_advertisement() {
214   std::string payload = ble_ad_list_.advertisements(packet_num_).payload();
215   std::string mac_address =
216       ble_ad_list_.advertisements(packet_num_).mac_address();
217   uint32_t delay_before_send_ms =
218       ble_ad_list_.advertisements(packet_num_).delay_before_send_ms();
219   next_ad_.ad.assign(payload.begin(), payload.end());
220   if (Address::IsValidAddress(mac_address)) {
221     // formatted string with colons like "12:34:56:78:9a:bc"
222     Address::FromString(mac_address, next_ad_.address);
223   } else if (mac_address.size() == Address::kLength) {
224     // six-byte binary address
225     std::vector<uint8_t> mac_vector(mac_address.cbegin(), mac_address.cend());
226     next_ad_.address.Address::FromOctets(mac_vector.data());
227   } else {
228     Address::FromString("BA:D0:AD:BA:D0:AD", next_ad_.address);
229   }
230   next_ad_.ad_time +=
231       steady_clock::duration(std::chrono::milliseconds(delay_before_send_ms));
232   packet_num_++;
233 }
234 }  // namespace test_vendor_lib
235