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