1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "core/internal/mediums/ble.h"
16 
17 #include <memory>
18 #include <string>
19 #include <utility>
20 
21 #include "core/internal/mediums/ble_v2/ble_advertisement.h"
22 #include "core/internal/mediums/utils.h"
23 #include "platform/base/prng.h"
24 #include "platform/public/logging.h"
25 #include "platform/public/mutex_lock.h"
26 
27 namespace location {
28 namespace nearby {
29 namespace connections {
30 
GenerateHash(const std::string & source,size_t size)31 ByteArray Ble::GenerateHash(const std::string& source, size_t size) {
32   return Utils::Sha256Hash(source, size);
33 }
34 
GenerateDeviceToken()35 ByteArray Ble::GenerateDeviceToken() {
36   return Utils::Sha256Hash(std::to_string(Prng().NextUint32()),
37                            mediums::BleAdvertisement::kDeviceTokenLength);
38 }
39 
Ble(BluetoothRadio & radio)40 Ble::Ble(BluetoothRadio& radio) : radio_(radio) {}
41 
IsAvailable() const42 bool Ble::IsAvailable() const {
43   MutexLock lock(&mutex_);
44 
45   return IsAvailableLocked();
46 }
47 
IsAvailableLocked() const48 bool Ble::IsAvailableLocked() const { return medium_.IsValid(); }
49 
StartAdvertising(const std::string & service_id,const ByteArray & advertisement_bytes,const std::string & fast_advertisement_service_uuid)50 bool Ble::StartAdvertising(const std::string& service_id,
51                            const ByteArray& advertisement_bytes,
52                            const std::string& fast_advertisement_service_uuid) {
53   MutexLock lock(&mutex_);
54 
55   if (advertisement_bytes.Empty()) {
56     NEARBY_LOGS(INFO)
57         << "Refusing to turn on BLE advertising. Empty advertisement data.";
58     return false;
59   }
60 
61   if (advertisement_bytes.size() > kMaxAdvertisementLength) {
62     NEARBY_LOG(INFO,
63                "Refusing to start BLE advertising because the advertisement "
64                "was too long. Expected at most %d bytes but received %d.",
65                kMaxAdvertisementLength, advertisement_bytes.size());
66     return false;
67   }
68 
69   if (IsAdvertisingLocked(service_id)) {
70     NEARBY_LOGS(INFO)
71         << "Failed to BLE advertise because we're already advertising.";
72     return false;
73   }
74 
75   if (!radio_.IsEnabled()) {
76     NEARBY_LOGS(INFO)
77         << "Can't start BLE scanning because Bluetooth was never turned on";
78     return false;
79   }
80 
81   if (!IsAvailableLocked()) {
82     NEARBY_LOGS(INFO) << "Can't turn on BLE advertising. BLE is not available.";
83     return false;
84   }
85 
86   NEARBY_LOGS(INFO) << "Turning on BLE advertising with advertisement bytes="
87                     << advertisement_bytes.data() << "("
88                     << advertisement_bytes.size() << ")"
89                     << ", service id=" << service_id
90                     << ", fast advertisement service uuid="
91                     << fast_advertisement_service_uuid;
92 
93   // Wrap the connections advertisement to the medium advertisement.
94   const bool fast_advertisement = !fast_advertisement_service_uuid.empty();
95   ByteArray service_id_hash{GenerateHash(
96       service_id, mediums::BleAdvertisement::kServiceIdHashLength)};
97   ByteArray medium_advertisement_bytes{mediums::BleAdvertisement{
98       mediums::BleAdvertisement::Version::kV2,
99       mediums::BleAdvertisement::SocketVersion::kV2,
100       fast_advertisement ? ByteArray{} : service_id_hash, advertisement_bytes,
101       GenerateDeviceToken()}};
102   if (medium_advertisement_bytes.Empty()) {
103     NEARBY_LOGS(INFO) << "Failed to BLE advertise because we could not "
104                          "create a medium advertisement.";
105     return false;
106   }
107 
108   if (!medium_.StartAdvertising(service_id, medium_advertisement_bytes,
109                                 fast_advertisement_service_uuid)) {
110     NEARBY_LOGS(INFO)
111         << "Failed to turn on BLE advertising with advertisement bytes="
112         << advertisement_bytes.data() << "(" << advertisement_bytes.size()
113         << ")"
114         << ", fast advertisement service uuid="
115         << fast_advertisement_service_uuid;
116     return false;
117   }
118 
119   advertising_info_.Add(service_id);
120   return true;
121 }
122 
StopAdvertising(const std::string & service_id)123 bool Ble::StopAdvertising(const std::string& service_id) {
124   MutexLock lock(&mutex_);
125 
126   if (!IsAdvertisingLocked(service_id)) {
127     NEARBY_LOGS(INFO) << "Can't turn off BLE advertising; it is already off";
128     return false;
129   }
130 
131   NEARBY_LOGS(INFO) << "Turned off BLE advertising with service id="
132                     << service_id;
133   bool ret = medium_.StopAdvertising(service_id);
134   // Reset our bundle of advertising state to mark that we're no longer
135   // advertising.
136   advertising_info_.Remove(service_id);
137   return ret;
138 }
139 
IsAdvertising(const std::string & service_id)140 bool Ble::IsAdvertising(const std::string& service_id) {
141   MutexLock lock(&mutex_);
142 
143   return IsAdvertisingLocked(service_id);
144 }
145 
IsAdvertisingLocked(const std::string & service_id)146 bool Ble::IsAdvertisingLocked(const std::string& service_id) {
147   return advertising_info_.Existed(service_id);
148 }
149 
StartScanning(const std::string & service_id,const std::string & fast_advertisement_service_uuid,DiscoveredPeripheralCallback callback)150 bool Ble::StartScanning(const std::string& service_id,
151                         const std::string& fast_advertisement_service_uuid,
152                         DiscoveredPeripheralCallback callback) {
153   MutexLock lock(&mutex_);
154 
155   discovered_peripheral_callback_ = std::move(callback);
156 
157   if (service_id.empty()) {
158     NEARBY_LOGS(INFO)
159         << "Refusing to start BLE scanning with empty service id.";
160     return false;
161   }
162 
163   if (IsScanningLocked(service_id)) {
164     NEARBY_LOGS(INFO) << "Refusing to start scan of BLE peripherals because "
165                          "another scanning is already in-progress.";
166     return false;
167   }
168 
169   if (!radio_.IsEnabled()) {
170     NEARBY_LOGS(INFO)
171         << "Can't start BLE scanning because Bluetooth was never turned on";
172     return false;
173   }
174 
175   if (!IsAvailableLocked()) {
176     NEARBY_LOGS(INFO)
177         << "Can't scan BLE peripherals because BLE isn't available.";
178     return false;
179   }
180 
181   if (!medium_.StartScanning(
182           service_id, fast_advertisement_service_uuid,
183           {
184               .peripheral_discovered_cb =
185                   [this](BlePeripheral& peripheral,
186                          const std::string& service_id,
187                          const ByteArray& medium_advertisement_bytes,
188                          bool fast_advertisement) {
189                     // Unwrap connection BleAdvertisement from medium
190                     // BleAdvertisement.
191                     auto connection_advertisement_bytes =
192                         UnwrapAdvertisementBytes(medium_advertisement_bytes);
193                     discovered_peripheral_callback_.peripheral_discovered_cb(
194                         peripheral, service_id, connection_advertisement_bytes,
195                         fast_advertisement);
196                   },
197               .peripheral_lost_cb =
198                   [this](BlePeripheral& peripheral,
199                          const std::string& service_id) {
200                     discovered_peripheral_callback_.peripheral_lost_cb(
201                         peripheral, service_id);
202                   },
203           })) {
204     NEARBY_LOGS(INFO) << "Failed to start scan of BLE services.";
205     return false;
206   }
207 
208   NEARBY_LOGS(INFO) << "Turned on BLE scanning with service id=" << service_id;
209   // Mark the fact that we're currently performing a BLE discovering.
210   scanning_info_.Add(service_id);
211   return true;
212 }
213 
StopScanning(const std::string & service_id)214 bool Ble::StopScanning(const std::string& service_id) {
215   MutexLock lock(&mutex_);
216 
217   if (!IsScanningLocked(service_id)) {
218     NEARBY_LOGS(INFO) << "Can't turn off BLE sacanning because we never "
219                          "started scanning.";
220     return false;
221   }
222 
223   NEARBY_LOG(INFO, "Turned off BLE scanning with service id=%s",
224              service_id.c_str());
225   bool ret = medium_.StopScanning(service_id);
226   scanning_info_.Clear();
227   return ret;
228 }
229 
IsScanning(const std::string & service_id)230 bool Ble::IsScanning(const std::string& service_id) {
231   MutexLock lock(&mutex_);
232 
233   return IsScanningLocked(service_id);
234 }
235 
IsScanningLocked(const std::string & service_id)236 bool Ble::IsScanningLocked(const std::string& service_id) {
237   return scanning_info_.Existed(service_id);
238 }
239 
StartAcceptingConnections(const std::string & service_id,AcceptedConnectionCallback callback)240 bool Ble::StartAcceptingConnections(const std::string& service_id,
241                                     AcceptedConnectionCallback callback) {
242   MutexLock lock(&mutex_);
243 
244   if (service_id.empty()) {
245     NEARBY_LOGS(INFO)
246         << "Refusing to start accepting BLE connections with empty service id.";
247     return false;
248   }
249 
250   if (IsAcceptingConnectionsLocked(service_id)) {
251     NEARBY_LOGS(INFO)
252         << "Refusing to start accepting BLE connections for "
253         << service_id
254         << " because another BLE peripheral socket is already in-progress.";
255     return false;
256   }
257 
258   if (!radio_.IsEnabled()) {
259     NEARBY_LOGS(INFO) << "Can't start accepting BLE connections for "
260                       << service_id
261                       << " because Bluetooth isn't enabled.";
262     return false;
263   }
264 
265   if (!IsAvailableLocked()) {
266     NEARBY_LOGS(INFO) << "Can't start accepting BLE connections for "
267                       << service_id << " because BLE isn't available.";
268     return false;
269   }
270 
271   if (!medium_.StartAcceptingConnections(service_id, callback)) {
272     NEARBY_LOGS(INFO) << "Failed to accept connections callback for "
273                       << service_id << " .";
274     return false;
275   }
276 
277   accepting_connections_info_.Add(service_id);
278   return true;
279 }
280 
StopAcceptingConnections(const std::string & service_id)281 bool Ble::StopAcceptingConnections(const std::string& service_id) {
282   MutexLock lock(&mutex_);
283 
284   if (!IsAcceptingConnectionsLocked(service_id)) {
285     NEARBY_LOGS(INFO)
286         << "Can't stop accepting BLE connections because it was never started.";
287     return false;
288   }
289 
290   bool ret = medium_.StopAcceptingConnections(service_id);
291   // Reset our bundle of accepting connections state to mark that we're no
292   // longer accepting connections.
293   accepting_connections_info_.Remove(service_id);
294   return ret;
295 }
296 
IsAcceptingConnections(const std::string & service_id)297 bool Ble::IsAcceptingConnections(const std::string& service_id) {
298   MutexLock lock(&mutex_);
299 
300   return IsAcceptingConnectionsLocked(service_id);
301 }
302 
IsAcceptingConnectionsLocked(const std::string & service_id)303 bool Ble::IsAcceptingConnectionsLocked(const std::string& service_id) {
304   return accepting_connections_info_.Existed(service_id);
305 }
306 
Connect(BlePeripheral & peripheral,const std::string & service_id)307 BleSocket Ble::Connect(BlePeripheral& peripheral,
308                        const std::string& service_id) {
309   MutexLock lock(&mutex_);
310   NEARBY_LOGS(INFO) << "BLE::Connect: service=" << &peripheral;
311   // Socket to return. To allow for NRVO to work, it has to be a single object.
312   BleSocket socket;
313 
314   if (service_id.empty()) {
315     NEARBY_LOGS(INFO) << "Refusing to create BLE socket with empty service_id.";
316     return socket;
317   }
318 
319   if (!radio_.IsEnabled()) {
320     NEARBY_LOGS(INFO) << "Can't create client BLE socket to "
321                       << &peripheral << " because Bluetooth isn't enabled.";
322     return socket;
323   }
324 
325   if (!IsAvailableLocked()) {
326     NEARBY_LOGS(INFO) << "Can't create client BLE socket [service_id="
327                       << service_id << "]; BLE isn't available.";
328     return socket;
329   }
330 
331   socket = medium_.Connect(peripheral, service_id);
332   if (!socket.IsValid()) {
333     NEARBY_LOGS(INFO) << "Failed to Connect via BLE [service=" << service_id
334                       << "]";
335   }
336 
337   return socket;
338 }
339 
UnwrapAdvertisementBytes(const ByteArray & medium_advertisement_data)340 ByteArray Ble::UnwrapAdvertisementBytes(
341     const ByteArray& medium_advertisement_data) {
342   mediums::BleAdvertisement medium_ble_advertisement{medium_advertisement_data};
343   if (!medium_ble_advertisement.IsValid()) {
344     return ByteArray{};
345   }
346 
347   return medium_ble_advertisement.GetData();
348 }
349 
350 }  // namespace connections
351 }  // namespace nearby
352 }  // namespace location
353