• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //  Copyright 2015 Google, Inc.
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 #define LOG_TAG "bt_bluetooth_host"
18 
19 #include "service/ipc/linux_ipc_host.h"
20 
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/ioctl.h>
25 #include <sys/socket.h>
26 #include <sys/types.h>
27 #include <sys/un.h>
28 #include <unistd.h>
29 
30 #include <algorithm>
31 
32 #include <base/base64.h>
33 #include <base/strings/string_number_conversions.h>
34 #include <base/strings/string_split.h>
35 
36 #include "osi/include/log.h"
37 #include "osi/include/osi.h"
38 #include "service/adapter.h"
39 
40 using bluetooth::Adapter;
41 using bluetooth::Uuid;
42 
43 using namespace bluetooth::gatt;
44 
45 namespace {
46 
47 // IPC API is according to:
48 // https://docs.google.com/document/d/1eRnku-jAyVU1wGJsLT2CzWi0-8bs2g49s1b3FR_GApM
49 const char kSetAdapterNameCommand[] = "set-device-name";
50 const char kCreateServiceCommand[] = "create-service";
51 const char kDestroyServiceCommand[] = "destroy-service";
52 const char kAddCharacteristicCommand[] = "add-characteristic";
53 const char kSetCharacteristicValueCommand[] = "set-characteristic-value";
54 const char kSetAdvertisementCommand[] = "set-advertisement";
55 const char kSetScanResponseCommand[] = "set-scan-response";
56 const char kStartServiceCommand[] = "start-service";
57 const char kStopServiceCommand[] = "stop-service";
58 const char kWriteCharacteristicCommand[] = "write-characteristic";
59 
60 // Useful values for indexing LinuxIPCHost::pfds_
61 // Not super general considering that we should be able to support
62 // many GATT FDs owned by one LinuxIPCHost.
63 enum {
64   kFdIpc = 0,
65   kFdGatt = 1,
66   kPossibleFds = 2,
67 };
68 
TokenBool(const std::string & text)69 bool TokenBool(const std::string& text) { return text == "true"; }
70 
71 }  // namespace
72 
73 namespace ipc {
74 
LinuxIPCHost(int sockfd,Adapter * adapter)75 LinuxIPCHost::LinuxIPCHost(int sockfd, Adapter* adapter)
76     : adapter_(adapter), pfds_(1, {sockfd, POLLIN, 0}) {}
77 
~LinuxIPCHost()78 LinuxIPCHost::~LinuxIPCHost() { close(pfds_[0].fd); }
79 
EventLoop()80 bool LinuxIPCHost::EventLoop() {
81   while (true) {
82     int status =
83         TEMP_FAILURE_RETRY(ppoll(pfds_.data(), pfds_.size(), nullptr, nullptr));
84     if (status < 1) {
85       LOG_ERROR(LOG_TAG, "ppoll error");
86       return false;
87     }
88 
89     if (pfds_[kFdIpc].revents && !OnMessage()) {
90       return false;
91     }
92 
93     if (pfds_.size() == kPossibleFds && pfds_[kFdGatt].revents &&
94         !OnGattWrite()) {
95       return false;
96     }
97   }
98   return true;
99 }
100 
OnSetAdapterName(const std::string & name)101 bool LinuxIPCHost::OnSetAdapterName(const std::string& name) {
102   std::string decoded_data;
103   base::Base64Decode(name, &decoded_data);
104   return adapter_->SetName(decoded_data);
105 }
106 
OnCreateService(const std::string & service_uuid)107 bool LinuxIPCHost::OnCreateService(const std::string& service_uuid) {
108   gatt_servers_[service_uuid] = std::unique_ptr<Server>(new Server);
109 
110   int gattfd;
111   bool status = gatt_servers_[service_uuid]->Initialize(
112       Uuid::FromString(service_uuid), &gattfd);
113   if (!status) {
114     LOG_ERROR(LOG_TAG, "Failed to initialize bluetooth");
115     return false;
116   }
117   pfds_.resize(kPossibleFds);
118   pfds_[kFdGatt] = {gattfd, POLLIN, 0};
119   return true;
120 }
121 
OnDestroyService(const std::string & service_uuid)122 bool LinuxIPCHost::OnDestroyService(const std::string& service_uuid) {
123   gatt_servers_.erase(service_uuid);
124   close(pfds_[1].fd);
125   pfds_.resize(1);
126   return true;
127 }
128 
OnAddCharacteristic(const std::string & service_uuid,const std::string & characteristic_uuid,const std::string & control_uuid,const std::string & options)129 bool LinuxIPCHost::OnAddCharacteristic(const std::string& service_uuid,
130                                        const std::string& characteristic_uuid,
131                                        const std::string& control_uuid,
132                                        const std::string& options) {
133   std::vector<std::string> option_tokens = base::SplitString(
134       options, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
135 
136   int properties_mask = 0;
137   int permissions_mask = 0;
138 
139   if (std::find(option_tokens.begin(), option_tokens.end(), "notify") !=
140       option_tokens.end()) {
141     permissions_mask |= kPermissionRead;
142     properties_mask |= kPropertyRead;
143     properties_mask |= kPropertyNotify;
144   }
145   if (std::find(option_tokens.begin(), option_tokens.end(), "read") !=
146       option_tokens.end()) {
147     permissions_mask |= kPermissionRead;
148     properties_mask |= kPropertyRead;
149   }
150   if (std::find(option_tokens.begin(), option_tokens.end(), "write") !=
151       option_tokens.end()) {
152     permissions_mask |= kPermissionWrite;
153     properties_mask |= kPropertyWrite;
154   }
155 
156   if (control_uuid.empty()) {
157     gatt_servers_[service_uuid]->AddCharacteristic(
158         Uuid::FromString(characteristic_uuid), properties_mask,
159         permissions_mask);
160   } else {
161     gatt_servers_[service_uuid]->AddBlob(Uuid::FromString(characteristic_uuid),
162                                          Uuid::FromString(control_uuid),
163                                          properties_mask, permissions_mask);
164   }
165   return true;
166 }
167 
OnSetCharacteristicValue(const std::string & service_uuid,const std::string & characteristic_uuid,const std::string & value)168 bool LinuxIPCHost::OnSetCharacteristicValue(
169     const std::string& service_uuid, const std::string& characteristic_uuid,
170     const std::string& value) {
171   std::string decoded_data;
172   base::Base64Decode(value, &decoded_data);
173   std::vector<uint8_t> blob_data(decoded_data.begin(), decoded_data.end());
174   gatt_servers_[service_uuid]->SetCharacteristicValue(
175       Uuid::FromString(characteristic_uuid), blob_data);
176   return true;
177 }
178 
OnSetAdvertisement(const std::string & service_uuid,const std::string & advertise_uuids,const std::string & advertise_data,const std::string & manufacturer_data,const std::string & transmit_name)179 bool LinuxIPCHost::OnSetAdvertisement(const std::string& service_uuid,
180                                       const std::string& advertise_uuids,
181                                       const std::string& advertise_data,
182                                       const std::string& manufacturer_data,
183                                       const std::string& transmit_name) {
184   LOG_INFO(LOG_TAG, "%s: service:%s uuids:%s data:%s", __func__,
185            service_uuid.c_str(), advertise_uuids.c_str(),
186            advertise_data.c_str());
187 
188   std::vector<std::string> advertise_uuid_tokens = base::SplitString(
189       advertise_uuids, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
190 
191   // string -> vector<Uuid>
192   std::vector<Uuid> ids;
193   for (const auto& uuid_token : advertise_uuid_tokens)
194     ids.emplace_back(Uuid::FromString(uuid_token));
195 
196   std::string decoded_data;
197   base::Base64Decode(advertise_data, &decoded_data);
198   std::vector<uint8_t> decoded_advertise_data(decoded_data.begin(),
199                                               decoded_data.end());
200 
201   base::Base64Decode(manufacturer_data, &decoded_data);
202   std::vector<uint8_t> decoded_manufacturer_data(decoded_data.begin(),
203                                                  decoded_data.end());
204 
205   gatt_servers_[service_uuid]->SetAdvertisement(ids, decoded_advertise_data,
206                                                 decoded_manufacturer_data,
207                                                 TokenBool(transmit_name));
208   return true;
209 }
210 
OnSetScanResponse(const std::string & service_uuid,const std::string & scan_response_uuids,const std::string & scan_response_data,const std::string & manufacturer_data,const std::string & transmit_name)211 bool LinuxIPCHost::OnSetScanResponse(const std::string& service_uuid,
212                                      const std::string& scan_response_uuids,
213                                      const std::string& scan_response_data,
214                                      const std::string& manufacturer_data,
215                                      const std::string& transmit_name) {
216   std::vector<std::string> scan_response_uuid_tokens = base::SplitString(
217       scan_response_uuids, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
218 
219   // string -> vector<Uuid>
220   std::vector<Uuid> ids;
221   for (const auto& uuid_token : scan_response_uuid_tokens)
222     ids.emplace_back(Uuid::FromString(uuid_token));
223 
224   std::string decoded_data;
225   base::Base64Decode(scan_response_data, &decoded_data);
226   std::vector<uint8_t> decoded_advertise_data(decoded_data.begin(),
227                                               decoded_data.end());
228 
229   base::Base64Decode(manufacturer_data, &decoded_data);
230   std::vector<uint8_t> decoded_manufacturer_data(decoded_data.begin(),
231                                                  decoded_data.end());
232 
233   gatt_servers_[service_uuid]->SetScanResponse(ids, decoded_advertise_data,
234                                                decoded_manufacturer_data,
235                                                TokenBool(transmit_name));
236   return true;
237 }
238 
OnStartService(const std::string & service_uuid)239 bool LinuxIPCHost::OnStartService(const std::string& service_uuid) {
240   return gatt_servers_[service_uuid]->Start();
241 }
242 
OnStopService(const std::string & service_uuid)243 bool LinuxIPCHost::OnStopService(const std::string& service_uuid) {
244   return gatt_servers_[service_uuid]->Stop();
245 }
246 
OnMessage()247 bool LinuxIPCHost::OnMessage() {
248   std::string ipc_msg;
249   ssize_t size;
250 
251   OSI_NO_INTR(size =
252                   recv(pfds_[kFdIpc].fd, &ipc_msg[0], 0, MSG_PEEK | MSG_TRUNC));
253   if (-1 == size) {
254     LOG_ERROR(LOG_TAG, "Error reading datagram size: %s", strerror(errno));
255     return false;
256   } else if (0 == size) {
257     LOG_INFO(LOG_TAG, "%s:%d: Connection closed", __func__, __LINE__);
258     return false;
259   }
260 
261   ipc_msg.resize(size);
262   OSI_NO_INTR(size = read(pfds_[kFdIpc].fd, &ipc_msg[0], ipc_msg.size()));
263   if (-1 == size) {
264     LOG_ERROR(LOG_TAG, "Error reading IPC: %s", strerror(errno));
265     return false;
266   } else if (0 == size) {
267     LOG_INFO(LOG_TAG, "%s:%d: Connection closed", __func__, __LINE__);
268     return false;
269   }
270 
271   std::vector<std::string> tokens = base::SplitString(
272       ipc_msg, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
273   switch (tokens.size()) {
274     case 2:
275       if (tokens[0] == kSetAdapterNameCommand)
276         return OnSetAdapterName(tokens[1]);
277       if (tokens[0] == kCreateServiceCommand) return OnCreateService(tokens[1]);
278       if (tokens[0] == kDestroyServiceCommand)
279         return OnDestroyService(tokens[1]);
280       if (tokens[0] == kStartServiceCommand) return OnStartService(tokens[1]);
281       if (tokens[0] == kStopServiceCommand) return OnStopService(tokens[1]);
282       break;
283     case 4:
284       if (tokens[0] == kSetCharacteristicValueCommand)
285         return OnSetCharacteristicValue(tokens[1], tokens[2], tokens[3]);
286       break;
287     case 5:
288       if (tokens[0] == kAddCharacteristicCommand)
289         return OnAddCharacteristic(tokens[1], tokens[2], tokens[3], tokens[4]);
290       break;
291     case 6:
292       if (tokens[0] == kSetAdvertisementCommand)
293         return OnSetAdvertisement(tokens[1], tokens[2], tokens[3], tokens[4],
294                                   tokens[5]);
295       if (tokens[0] == kSetScanResponseCommand)
296         return OnSetScanResponse(tokens[1], tokens[2], tokens[3], tokens[4],
297                                  tokens[5]);
298       break;
299     default:
300       break;
301   }
302 
303   LOG_ERROR(LOG_TAG, "Malformed IPC message: %s", ipc_msg.c_str());
304   return false;
305 }
306 
OnGattWrite()307 bool LinuxIPCHost::OnGattWrite() {
308   Uuid::UUID128Bit id;
309   ssize_t r;
310 
311   OSI_NO_INTR(r = read(pfds_[kFdGatt].fd, id.data(), id.size()));
312   if (r != id.size()) {
313     LOG_ERROR(LOG_TAG, "Error reading GATT attribute ID");
314     return false;
315   }
316 
317   std::vector<uint8_t> value;
318   // TODO(icoolidge): Generalize this for multiple clients.
319   auto server = gatt_servers_.begin();
320   server->second->GetCharacteristicValue(Uuid::From128BitBE(id), &value);
321   const std::string value_string(value.begin(), value.end());
322   std::string encoded_value;
323   base::Base64Encode(value_string, &encoded_value);
324 
325   std::string transmit(kWriteCharacteristicCommand);
326   transmit += "|" + server->first;
327   transmit += "|" + base::HexEncode(id.data(), id.size());
328   transmit += "|" + encoded_value;
329 
330   OSI_NO_INTR(r = write(pfds_[kFdIpc].fd, transmit.data(), transmit.size()));
331   if (-1 == r) {
332     LOG_ERROR(LOG_TAG, "Error replying to IPC: %s", strerror(errno));
333     return false;
334   }
335 
336   return true;
337 }
338 
339 }  // namespace ipc
340