linux_ipc_host.cc revision 911d1ae03efec2d54c3b1b605589d790d1745488
1//
2//  Copyright (C) 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
40using bluetooth::Adapter;
41using bluetooth::UUID;
42
43using namespace bluetooth::gatt;
44
45namespace {
46
47// IPC API is according to:
48// https://docs.google.com/document/d/1eRnku-jAyVU1wGJsLT2CzWi0-8bs2g49s1b3FR_GApM
49const char kSetAdapterNameCommand[] = "set-device-name";
50const char kCreateServiceCommand[] = "create-service";
51const char kDestroyServiceCommand[] = "destroy-service";
52const char kAddCharacteristicCommand[] = "add-characteristic";
53const char kSetCharacteristicValueCommand[] = "set-characteristic-value";
54const char kSetAdvertisementCommand[] = "set-advertisement";
55const char kSetScanResponseCommand[] = "set-scan-response";
56const char kStartServiceCommand[] = "start-service";
57const char kStopServiceCommand[] = "stop-service";
58const 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.
63enum {
64  kFdIpc = 0,
65  kFdGatt = 1,
66  kPossibleFds = 2,
67};
68
69bool TokenBool(const std::string& text) { return text == "true"; }
70
71}  // namespace
72
73namespace ipc {
74
75LinuxIPCHost::LinuxIPCHost(int sockfd, Adapter* adapter)
76    : adapter_(adapter), pfds_(1, {sockfd, POLLIN, 0}) {}
77
78LinuxIPCHost::~LinuxIPCHost() { close(pfds_[0].fd); }
79
80bool 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
101bool 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
107bool 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 =
112      gatt_servers_[service_uuid]->Initialize(UUID(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
122bool 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
129bool 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(characteristic_uuid), properties_mask, permissions_mask);
159  } else {
160    gatt_servers_[service_uuid]->AddBlob(UUID(characteristic_uuid),
161                                         UUID(control_uuid), properties_mask,
162                                         permissions_mask);
163  }
164  return true;
165}
166
167bool LinuxIPCHost::OnSetCharacteristicValue(
168    const std::string& service_uuid, const std::string& characteristic_uuid,
169    const std::string& value) {
170  std::string decoded_data;
171  base::Base64Decode(value, &decoded_data);
172  std::vector<uint8_t> blob_data(decoded_data.begin(), decoded_data.end());
173  gatt_servers_[service_uuid]->SetCharacteristicValue(UUID(characteristic_uuid),
174                                                      blob_data);
175  return true;
176}
177
178bool LinuxIPCHost::OnSetAdvertisement(const std::string& service_uuid,
179                                      const std::string& advertise_uuids,
180                                      const std::string& advertise_data,
181                                      const std::string& manufacturer_data,
182                                      const std::string& transmit_name) {
183  LOG_INFO(LOG_TAG, "%s: service:%s uuids:%s data:%s", __func__,
184           service_uuid.c_str(), advertise_uuids.c_str(),
185           advertise_data.c_str());
186
187  std::vector<std::string> advertise_uuid_tokens = base::SplitString(
188      advertise_uuids, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
189
190  // string -> vector<UUID>
191  std::vector<UUID> ids;
192  for (const auto& uuid_token : advertise_uuid_tokens)
193    ids.emplace_back(uuid_token);
194
195  std::string decoded_data;
196  base::Base64Decode(advertise_data, &decoded_data);
197  std::vector<uint8_t> decoded_advertise_data(decoded_data.begin(),
198                                              decoded_data.end());
199
200  base::Base64Decode(manufacturer_data, &decoded_data);
201  std::vector<uint8_t> decoded_manufacturer_data(decoded_data.begin(),
202                                                 decoded_data.end());
203
204  gatt_servers_[service_uuid]->SetAdvertisement(ids, decoded_advertise_data,
205                                                decoded_manufacturer_data,
206                                                TokenBool(transmit_name));
207  return true;
208}
209
210bool LinuxIPCHost::OnSetScanResponse(const std::string& service_uuid,
211                                     const std::string& scan_response_uuids,
212                                     const std::string& scan_response_data,
213                                     const std::string& manufacturer_data,
214                                     const std::string& transmit_name) {
215  std::vector<std::string> scan_response_uuid_tokens = base::SplitString(
216      scan_response_uuids, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
217
218  // string -> vector<UUID>
219  std::vector<UUID> ids;
220  for (const auto& uuid_token : scan_response_uuid_tokens)
221    ids.emplace_back(uuid_token);
222
223  std::string decoded_data;
224  base::Base64Decode(scan_response_data, &decoded_data);
225  std::vector<uint8_t> decoded_advertise_data(decoded_data.begin(),
226                                              decoded_data.end());
227
228  base::Base64Decode(manufacturer_data, &decoded_data);
229  std::vector<uint8_t> decoded_manufacturer_data(decoded_data.begin(),
230                                                 decoded_data.end());
231
232  gatt_servers_[service_uuid]->SetScanResponse(ids, decoded_advertise_data,
233                                               decoded_manufacturer_data,
234                                               TokenBool(transmit_name));
235  return true;
236}
237
238bool LinuxIPCHost::OnStartService(const std::string& service_uuid) {
239  return gatt_servers_[service_uuid]->Start();
240}
241
242bool LinuxIPCHost::OnStopService(const std::string& service_uuid) {
243  return gatt_servers_[service_uuid]->Stop();
244}
245
246bool LinuxIPCHost::OnMessage() {
247  std::string ipc_msg;
248  ssize_t size;
249
250  OSI_NO_INTR(size =
251                  recv(pfds_[kFdIpc].fd, &ipc_msg[0], 0, MSG_PEEK | MSG_TRUNC));
252  if (-1 == size) {
253    LOG_ERROR(LOG_TAG, "Error reading datagram size: %s", strerror(errno));
254    return false;
255  } else if (0 == size) {
256    LOG_INFO(LOG_TAG, "%s:%d: Connection closed", __func__, __LINE__);
257    return false;
258  }
259
260  ipc_msg.resize(size);
261  OSI_NO_INTR(size = read(pfds_[kFdIpc].fd, &ipc_msg[0], ipc_msg.size()));
262  if (-1 == size) {
263    LOG_ERROR(LOG_TAG, "Error reading IPC: %s", strerror(errno));
264    return false;
265  } else if (0 == size) {
266    LOG_INFO(LOG_TAG, "%s:%d: Connection closed", __func__, __LINE__);
267    return false;
268  }
269
270  std::vector<std::string> tokens = base::SplitString(
271      ipc_msg, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
272  switch (tokens.size()) {
273    case 2:
274      if (tokens[0] == kSetAdapterNameCommand)
275        return OnSetAdapterName(tokens[1]);
276      if (tokens[0] == kCreateServiceCommand) return OnCreateService(tokens[1]);
277      if (tokens[0] == kDestroyServiceCommand)
278        return OnDestroyService(tokens[1]);
279      if (tokens[0] == kStartServiceCommand) return OnStartService(tokens[1]);
280      if (tokens[0] == kStopServiceCommand) return OnStopService(tokens[1]);
281      break;
282    case 4:
283      if (tokens[0] == kSetCharacteristicValueCommand)
284        return OnSetCharacteristicValue(tokens[1], tokens[2], tokens[3]);
285      break;
286    case 5:
287      if (tokens[0] == kAddCharacteristicCommand)
288        return OnAddCharacteristic(tokens[1], tokens[2], tokens[3], tokens[4]);
289      break;
290    case 6:
291      if (tokens[0] == kSetAdvertisementCommand)
292        return OnSetAdvertisement(tokens[1], tokens[2], tokens[3], tokens[4],
293                                  tokens[5]);
294      if (tokens[0] == kSetScanResponseCommand)
295        return OnSetScanResponse(tokens[1], tokens[2], tokens[3], tokens[4],
296                                 tokens[5]);
297      break;
298    default:
299      break;
300  }
301
302  LOG_ERROR(LOG_TAG, "Malformed IPC message: %s", ipc_msg.c_str());
303  return false;
304}
305
306bool LinuxIPCHost::OnGattWrite() {
307  UUID::UUID128Bit id;
308  ssize_t r;
309
310  OSI_NO_INTR(r = read(pfds_[kFdGatt].fd, id.data(), id.size()));
311  if (r != id.size()) {
312    LOG_ERROR(LOG_TAG, "Error reading GATT attribute ID");
313    return false;
314  }
315
316  std::vector<uint8_t> value;
317  // TODO(icoolidge): Generalize this for multiple clients.
318  auto server = gatt_servers_.begin();
319  server->second->GetCharacteristicValue(UUID(id), &value);
320  const std::string value_string(value.begin(), value.end());
321  std::string encoded_value;
322  base::Base64Encode(value_string, &encoded_value);
323
324  std::string transmit(kWriteCharacteristicCommand);
325  transmit += "|" + server->first;
326  transmit += "|" + base::HexEncode(id.data(), id.size());
327  transmit += "|" + encoded_value;
328
329  OSI_NO_INTR(r = write(pfds_[kFdIpc].fd, transmit.data(), transmit.size()));
330  if (-1 == r) {
331    LOG_ERROR(LOG_TAG, "Error replying to IPC: %s", strerror(errno));
332    return false;
333  }
334
335  return true;
336}
337
338}  // namespace ipc
339