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/osi.h"
37 #include "osi/include/log.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) {
70 return text == "true";
71 }
72
73 } // namespace
74
75 namespace ipc {
76
LinuxIPCHost(int sockfd,Adapter * adapter)77 LinuxIPCHost::LinuxIPCHost(int sockfd, Adapter* adapter)
78 : adapter_(adapter), pfds_(1, {sockfd, POLLIN, 0}) {}
79
~LinuxIPCHost()80 LinuxIPCHost::~LinuxIPCHost() {
81 close(pfds_[0].fd);
82 }
83
EventLoop()84 bool LinuxIPCHost::EventLoop() {
85 while (true) {
86 int status =
87 TEMP_FAILURE_RETRY(ppoll(pfds_.data(), pfds_.size(), nullptr, nullptr));
88 if (status < 1) {
89 LOG_ERROR(LOG_TAG, "ppoll error");
90 return false;
91 }
92
93 if (pfds_[kFdIpc].revents && !OnMessage()) {
94 return false;
95 }
96
97 if (pfds_.size() == kPossibleFds &&
98 pfds_[kFdGatt].revents &&
99 !OnGattWrite()) {
100 return false;
101 }
102 }
103 return true;
104 }
105
OnSetAdapterName(const std::string & name)106 bool LinuxIPCHost::OnSetAdapterName(const std::string& name) {
107 std::string decoded_data;
108 base::Base64Decode(name, &decoded_data);
109 return adapter_->SetName(decoded_data);
110 }
111
OnCreateService(const std::string & service_uuid)112 bool LinuxIPCHost::OnCreateService(const std::string& service_uuid) {
113 gatt_servers_[service_uuid] = std::unique_ptr<Server>(new Server);
114
115 int gattfd;
116 bool status = gatt_servers_[service_uuid]->Initialize(
117 UUID(service_uuid), &gattfd);
118 if (!status) {
119 LOG_ERROR(LOG_TAG, "Failed to initialize bluetooth");
120 return false;
121 }
122 pfds_.resize(kPossibleFds);
123 pfds_[kFdGatt] = {gattfd, POLLIN, 0};
124 return true;
125 }
126
OnDestroyService(const std::string & service_uuid)127 bool LinuxIPCHost::OnDestroyService(const std::string& service_uuid) {
128 gatt_servers_.erase(service_uuid);
129 close(pfds_[1].fd);
130 pfds_.resize(1);
131 return true;
132 }
133
OnAddCharacteristic(const std::string & service_uuid,const std::string & characteristic_uuid,const std::string & control_uuid,const std::string & options)134 bool LinuxIPCHost::OnAddCharacteristic(const std::string& service_uuid,
135 const std::string& characteristic_uuid,
136 const std::string& control_uuid,
137 const std::string& options) {
138 std::vector<std::string> option_tokens = base::SplitString(
139 options, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
140
141 int properties_mask = 0;
142 int permissions_mask = 0;
143
144 if (std::find(option_tokens.begin(), option_tokens.end(), "notify") !=
145 option_tokens.end()) {
146 permissions_mask |= kPermissionRead;
147 properties_mask |= kPropertyRead;
148 properties_mask |= kPropertyNotify;
149 }
150 if (std::find(option_tokens.begin(), option_tokens.end(), "read") !=
151 option_tokens.end()) {
152 permissions_mask |= kPermissionRead;
153 properties_mask |= kPropertyRead;
154 }
155 if (std::find(option_tokens.begin(), option_tokens.end(), "write") !=
156 option_tokens.end()) {
157 permissions_mask |= kPermissionWrite;
158 properties_mask |= kPropertyWrite;
159 }
160
161 if (control_uuid.empty()) {
162 gatt_servers_[service_uuid]->AddCharacteristic(
163 UUID(characteristic_uuid), properties_mask, permissions_mask);
164 } else {
165 gatt_servers_[service_uuid]->AddBlob(UUID(characteristic_uuid),
166 UUID(control_uuid), properties_mask,
167 permissions_mask);
168 }
169 return true;
170 }
171
OnSetCharacteristicValue(const std::string & service_uuid,const std::string & characteristic_uuid,const std::string & value)172 bool LinuxIPCHost::OnSetCharacteristicValue(const std::string& service_uuid,
173 const std::string& characteristic_uuid,
174 const std::string& value) {
175 std::string decoded_data;
176 base::Base64Decode(value, &decoded_data);
177 std::vector<uint8_t> blob_data(decoded_data.begin(), decoded_data.end());
178 gatt_servers_[service_uuid]->SetCharacteristicValue(UUID(characteristic_uuid),
179 blob_data);
180 return true;
181 }
182
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)183 bool LinuxIPCHost::OnSetAdvertisement(const std::string& service_uuid,
184 const std::string& advertise_uuids,
185 const std::string& advertise_data,
186 const std::string& manufacturer_data,
187 const std::string& transmit_name) {
188 LOG_INFO(LOG_TAG, "%s: service:%s uuids:%s data:%s", __func__, service_uuid.c_str(),
189 advertise_uuids.c_str(), advertise_data.c_str());
190
191 std::vector<std::string> advertise_uuid_tokens = base::SplitString(
192 advertise_uuids, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
193
194 // string -> vector<UUID>
195 std::vector<UUID> ids;
196 for (const auto& uuid_token : advertise_uuid_tokens)
197 ids.emplace_back(uuid_token);
198
199 std::string decoded_data;
200 base::Base64Decode(advertise_data, &decoded_data);
201 std::vector<uint8_t> decoded_advertise_data(decoded_data.begin(),
202 decoded_data.end());
203
204 base::Base64Decode(manufacturer_data, &decoded_data);
205 std::vector<uint8_t> decoded_manufacturer_data(decoded_data.begin(),
206 decoded_data.end());
207
208 gatt_servers_[service_uuid]->SetAdvertisement(ids, decoded_advertise_data,
209 decoded_manufacturer_data,
210 TokenBool(transmit_name));
211 return true;
212 }
213
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)214 bool LinuxIPCHost::OnSetScanResponse(const std::string& service_uuid,
215 const std::string& scan_response_uuids,
216 const std::string& scan_response_data,
217 const std::string& manufacturer_data,
218 const std::string& transmit_name) {
219 std::vector<std::string> scan_response_uuid_tokens = base::SplitString(
220 scan_response_uuids, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
221
222 // string -> vector<UUID>
223 std::vector<UUID> ids;
224 for (const auto& uuid_token : scan_response_uuid_tokens)
225 ids.emplace_back(uuid_token);
226
227 std::string decoded_data;
228 base::Base64Decode(scan_response_data, &decoded_data);
229 std::vector<uint8_t> decoded_advertise_data(decoded_data.begin(),
230 decoded_data.end());
231
232 base::Base64Decode(manufacturer_data, &decoded_data);
233 std::vector<uint8_t> decoded_manufacturer_data(decoded_data.begin(),
234 decoded_data.end());
235
236 gatt_servers_[service_uuid]->SetScanResponse(ids, decoded_advertise_data,
237 decoded_manufacturer_data,
238 TokenBool(transmit_name));
239 return true;
240 }
241
OnStartService(const std::string & service_uuid)242 bool LinuxIPCHost::OnStartService(const std::string& service_uuid) {
243 return gatt_servers_[service_uuid]->Start();
244 }
245
OnStopService(const std::string & service_uuid)246 bool LinuxIPCHost::OnStopService(const std::string& service_uuid) {
247 return gatt_servers_[service_uuid]->Stop();
248 }
249
OnMessage()250 bool LinuxIPCHost::OnMessage() {
251 std::string ipc_msg;
252 ssize_t size;
253
254 OSI_NO_INTR(size = recv(pfds_[kFdIpc].fd, &ipc_msg[0], 0,
255 MSG_PEEK | MSG_TRUNC));
256 if (-1 == size) {
257 LOG_ERROR(LOG_TAG, "Error reading datagram size: %s", strerror(errno));
258 return false;
259 } else if (0 == size) {
260 LOG_INFO(LOG_TAG, "%s:%d: Connection closed", __func__, __LINE__);
261 return false;
262 }
263
264 ipc_msg.resize(size);
265 OSI_NO_INTR(size = read(pfds_[kFdIpc].fd, &ipc_msg[0], ipc_msg.size()));
266 if (-1 == size) {
267 LOG_ERROR(LOG_TAG, "Error reading IPC: %s", strerror(errno));
268 return false;
269 } else if (0 == size) {
270 LOG_INFO(LOG_TAG, "%s:%d: Connection closed", __func__, __LINE__);
271 return false;
272 }
273
274 std::vector<std::string> tokens = base::SplitString(
275 ipc_msg, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
276 switch (tokens.size()) {
277 case 2:
278 if (tokens[0] == kSetAdapterNameCommand)
279 return OnSetAdapterName(tokens[1]);
280 if (tokens[0] == kCreateServiceCommand)
281 return OnCreateService(tokens[1]);
282 if (tokens[0] == kDestroyServiceCommand)
283 return OnDestroyService(tokens[1]);
284 if (tokens[0] == kStartServiceCommand)
285 return OnStartService(tokens[1]);
286 if (tokens[0] == kStopServiceCommand)
287 return OnStopService(tokens[1]);
288 break;
289 case 4:
290 if (tokens[0] == kSetCharacteristicValueCommand)
291 return OnSetCharacteristicValue(tokens[1], tokens[2], tokens[3]);
292 break;
293 case 5:
294 if (tokens[0] == kAddCharacteristicCommand)
295 return OnAddCharacteristic(tokens[1], tokens[2], tokens[3], tokens[4]);
296 break;
297 case 6:
298 if (tokens[0] == kSetAdvertisementCommand)
299 return OnSetAdvertisement(tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
300 if (tokens[0] == kSetScanResponseCommand)
301 return OnSetScanResponse(tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
302 break;
303 default:
304 break;
305 }
306
307 LOG_ERROR(LOG_TAG, "Malformed IPC message: %s", ipc_msg.c_str());
308 return false;
309 }
310
OnGattWrite()311 bool LinuxIPCHost::OnGattWrite() {
312 UUID::UUID128Bit id;
313 ssize_t r;
314
315 OSI_NO_INTR(r = read(pfds_[kFdGatt].fd, id.data(), id.size()));
316 if (r != id.size()) {
317 LOG_ERROR(LOG_TAG, "Error reading GATT attribute ID");
318 return false;
319 }
320
321 std::vector<uint8_t> value;
322 // TODO(icoolidge): Generalize this for multiple clients.
323 auto server = gatt_servers_.begin();
324 server->second->GetCharacteristicValue(UUID(id), &value);
325 const std::string value_string(value.begin(), value.end());
326 std::string encoded_value;
327 base::Base64Encode(value_string, &encoded_value);
328
329 std::string transmit(kWriteCharacteristicCommand);
330 transmit += "|" + server->first;
331 transmit += "|" + base::HexEncode(id.data(), id.size());
332 transmit += "|" + encoded_value;
333
334 OSI_NO_INTR(r = write(pfds_[kFdIpc].fd, transmit.data(), transmit.size()));
335 if (-1 == r) {
336 LOG_ERROR(LOG_TAG, "Error replying to IPC: %s", strerror(errno));
337 return false;
338 }
339
340 return true;
341 }
342
343 } // namespace ipc
344