diff options
author | 2015-06-03 17:20:30 -0700 | |
---|---|---|
committer | 2015-06-12 15:40:40 -0700 | |
commit | a7358f3725625f334dcc62dd3224ce83db5f8342 (patch) | |
tree | 5b1e64efd59384e738e3bca51949486be57f8d00 | |
parent | 913deefe775259d2b23a4e454f2aba861a92e472 (diff) |
Add bthost, a simple BLE GATT server.
This is accessible via Unix socket.
It only has a couple of interesting features:
* Built in support for large blob attributes (>512 octets)
* Attribute caching (avoid frame-level IO for IPC clients)
Some string utilies are taken from modp_b64 and Chromium base.
Bug: 21076037
Change-Id: I6a29959159de76f8dd68d6bbaabe2100daabb6fa
26 files changed, 3507 insertions, 0 deletions
diff --git a/system/service/Android.mk b/system/service/Android.mk new file mode 100644 index 0000000000..f450840b60 --- /dev/null +++ b/system/service/Android.mk @@ -0,0 +1,59 @@ +# +# Copyright (C) 2015 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) + +BASE_SRC := \ + base/base64.cpp \ + base/string_split.cpp \ + base/string_number_conversions.cpp \ + modp_b64/modp_b64.cpp + +CORE_SRC := \ + a2dp_source.cpp \ + logging_helpers.cpp \ + core_stack.cpp \ + uuid.cpp \ + gatt_server.cpp + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := uuid_test.cpp uuid.cpp +LOCAL_CFLAGS += -std=c++11 +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := uuid_test_bd +include $(BUILD_HOST_NATIVE_TEST) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(BASE_SRC) \ + $(CORE_SRC) \ + host.cpp \ + main.cpp + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/../ + +LOCAL_CFLAGS += -std=c++11 +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bthost +LOCAL_REQUIRED_MODULES = bluetooth.default +LOCAL_SHARED_LIBRARIES += \ + libhardware \ + libcutils \ + liblog + +include $(BUILD_EXECUTABLE) diff --git a/system/service/a2dp_source.cpp b/system/service/a2dp_source.cpp new file mode 100644 index 0000000000..6cde92ae78 --- /dev/null +++ b/system/service/a2dp_source.cpp @@ -0,0 +1,82 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "a2dp_source.h" + +#define LOG_TAG "A2dpSource" +#include "osi/include/log.h" + +#include "core_stack.h" +#include "logging_helpers.h" +#include "osi/include/osi.h" + +namespace { + +void ConnectionStateCallback(btav_connection_state_t state, + UNUSED_ATTR bt_bdaddr_t *bd_addr) { + LOG_INFO("%s: %s", __func__, BtAvConnectionStateText(state)); +} + +void AudioStateCallback(btav_audio_state_t state, + UNUSED_ATTR bt_bdaddr_t *bd_addr) { + LOG_INFO("%s: %s", __func__, BtAvAudioStateText(state)); +} + +void AudioConfigCallback(UNUSED_ATTR bt_bdaddr_t *bd_addr, + UNUSED_ATTR uint32_t sample_rate, + UNUSED_ATTR uint8_t channel_count) { + // I think these are used for audio sink only? + // TODO(icoolidge): revisit. +} + +btav_callbacks_t av_callbacks = { + sizeof(btav_callbacks_t), ConnectionStateCallback, AudioStateCallback, + AudioConfigCallback, +}; + +} // namespace + +namespace bluetooth { + +A2dpSource::A2dpSource(CoreStack *bt) : av_(nullptr), bt_(bt) { + // TODO(icoolidge): DCHECK(bt); +} + +int A2dpSource::Start() { + // Get the interface to the a2dp source profile. + const void *interface = bt_->GetInterface(BT_PROFILE_ADVANCED_AUDIO_ID); + if (!interface) { + LOG_ERROR("Error getting audio source interface"); + return -1; + } + + av_ = reinterpret_cast<const btav_interface_t *>(interface); + + bt_status_t btstat = av_->init(&av_callbacks); + if (btstat != BT_STATUS_SUCCESS && btstat != BT_STATUS_DONE) { + LOG_ERROR("Failed to initialize audio source interface: %s %d", + BtStatusText(btstat), btstat); + return -1; + } + return 0; +} + +int A2dpSource::Stop() { + // TODO(icoolidge): DCHECK(av_); + av_->cleanup(); + return 0; +} + +} // namespace bluetooth diff --git a/system/service/a2dp_source.h b/system/service/a2dp_source.h new file mode 100644 index 0000000000..557b98fbcb --- /dev/null +++ b/system/service/a2dp_source.h @@ -0,0 +1,44 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include "hardware/bluetooth.h" +#include "hardware/bt_av.h" + +namespace bluetooth { + +class CoreStack; + +// This class is just experimental to test out BlueDroid A2DP +// interface, capability, and functionality. +class A2dpSource { + public: + explicit A2dpSource(CoreStack* bt); + + // Enables the A2DP source profile in the stack. + // Creates audio Unix sockets. (see audio_a2dp_hw.h) + int Start(); + + // Disables the A2DP source profile in the stack. + int Stop(); + + private: + const btav_interface_t* av_; + // Weak reference. + CoreStack* bt_; +}; + +} // namespace bluetooth diff --git a/system/service/base/base64.cpp b/system/service/base/base64.cpp new file mode 100644 index 0000000000..704d6789d5 --- /dev/null +++ b/system/service/base/base64.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base64.h" + +#include "modp_b64/modp_b64.h" + +namespace base { + +void Base64Encode(const std::string& input, std::string* output) { + std::string temp; + temp.resize(modp_b64_encode_len(input.size())); // makes room for null byte + + // modp_b64_encode_len() returns at least 1, so temp[0] is safe to use. + size_t output_size = modp_b64_encode(&(temp[0]), input.data(), input.size()); + + temp.resize(output_size); // strips off null byte + output->swap(temp); +} + +bool Base64Decode(const std::string& input, std::string* output) { + std::string temp; + temp.resize(modp_b64_decode_len(input.size())); + + // does not null terminate result since result is binary data! + size_t input_size = input.size(); + size_t output_size = modp_b64_decode(&(temp[0]), input.data(), input_size); + if (output_size == MODP_B64_ERROR) + return false; + + temp.resize(output_size); + output->swap(temp); + return true; +} + +} // namespace base diff --git a/system/service/base/base64.h b/system/service/base/base64.h new file mode 100644 index 0000000000..38923affbb --- /dev/null +++ b/system/service/base/base64.h @@ -0,0 +1,22 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASE64_H__ +#define BASE_BASE64_H__ + +#include <string> + +namespace base { + +// Encodes the input string in base64. The encoding can be done in-place. +void Base64Encode(const std::string& input, std::string* output); + +// Decodes the base64 input string. Returns true if successful and false +// otherwise. The output string is only modified if successful. The decoding can +// be done in-place. +bool Base64Decode(const std::string& input, std::string* output); + +} // namespace base + +#endif // BASE_BASE64_H__ diff --git a/system/service/base/string_number_conversions.cpp b/system/service/base/string_number_conversions.cpp new file mode 100644 index 0000000000..62c3d48347 --- /dev/null +++ b/system/service/base/string_number_conversions.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string_number_conversions.h" + +#include <stdlib.h> + +#include <limits> + +namespace base { + +// Utility to convert a character to a digit in a given base +template<typename CHAR, int BASE, bool BASE_LTE_10> class BaseCharToDigit { +}; + +// Faster specialization for bases <= 10 +template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, true> { + public: + static bool Convert(CHAR c, uint8_t* digit) { + if (c >= '0' && c < '0' + BASE) { + *digit = static_cast<uint8_t>(c - '0'); + return true; + } + return false; + } +}; + +// Specialization for bases where 10 < base <= 36 +template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, false> { + public: + static bool Convert(CHAR c, uint8_t* digit) { + if (c >= '0' && c <= '9') { + *digit = c - '0'; + } else if (c >= 'a' && c < 'a' + BASE - 10) { + *digit = c - 'a' + 10; + } else if (c >= 'A' && c < 'A' + BASE - 10) { + *digit = c - 'A' + 10; + } else { + return false; + } + return true; + } +}; + +template<int BASE, typename CHAR> bool CharToDigit(CHAR c, uint8_t* digit) { + return BaseCharToDigit<CHAR, BASE, BASE <= 10>::Convert(c, digit); +} + +template<typename STR> +bool HexStringToBytesT(const STR& input, std::vector<uint8_t>* output) { + output->clear(); + size_t count = input.size(); + if (count == 0 || (count % 2) != 0) + return false; + for (uintptr_t i = 0; i < count / 2; ++i) { + uint8_t msb = 0; // most significant 4 bits + uint8_t lsb = 0; // least significant 4 bits + if (!CharToDigit<16>(input[i * 2], &msb) || + !CharToDigit<16>(input[i * 2 + 1], &lsb)) + return false; + output->push_back((msb << 4) | lsb); + } + return true; +} + + +std::string HexEncode(const void* bytes, size_t size) { + static const char kHexChars[] = "0123456789ABCDEF"; + + // Each input byte creates two output hex characters. + std::string ret(size * 2, '\0'); + + for (size_t i = 0; i < size; ++i) { + char b = reinterpret_cast<const char*>(bytes)[i]; + ret[(i * 2)] = kHexChars[(b >> 4) & 0xf]; + ret[(i * 2) + 1] = kHexChars[b & 0xf]; + } + return ret; +} + + +bool HexStringToBytes(const std::string& input, std::vector<uint8_t>* output) { + return HexStringToBytesT(input, output); +} + +} // namespace base diff --git a/system/service/base/string_number_conversions.h b/system/service/base/string_number_conversions.h new file mode 100644 index 0000000000..7d1d7378db --- /dev/null +++ b/system/service/base/string_number_conversions.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_ +#define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_ + +#include <string> +#include <vector> + +namespace base { + +// Hex encoding ---------------------------------------------------------------- + +// Returns a hex string representation of a binary buffer. The returned hex +// string will be in upper case. This function does not check if |size| is +// within reasonable limits since it's written with trusted data in mind. If +// you suspect that the data you want to format might be large, the absolute +// max size for |size| should be is +// std::numeric_limits<size_t>::max() / 2 +std::string HexEncode(const void* bytes, size_t size); + +// Similar to the previous functions, except that output is a vector of bytes. +// |*output| will contain as many bytes as were successfully parsed prior to the +// error. There is no overflow, but input.size() must be evenly divisible by 2. +// Leading 0x or +/- are not allowed. +bool HexStringToBytes(const std::string& input, + std::vector<uint8_t>* output); + +} // namespace base + +#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_ diff --git a/system/service/base/string_split.cpp b/system/service/base/string_split.cpp new file mode 100644 index 0000000000..670f2e4840 --- /dev/null +++ b/system/service/base/string_split.cpp @@ -0,0 +1,36 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "string_split.h" + +namespace base { + +std::vector<std::string> SplitString(const std::string& input, char delimiter) { + std::vector<std::string> output; + size_t token_start = 0; + for (size_t i = 0; i <= input.size(); ++i) { + // Token is the whole string if no delimiter found. + if (i == input.size() || input[i] == delimiter) { + size_t token_length = i - token_start; + // Qualified tokens: substring, end text after token, non-empty whole input. + if (i != input.size() || !output.empty() || token_length) + output.emplace_back(input, token_start, token_length); + token_start = i + 1; + } + } + return output; +} + +} // namespace base diff --git a/system/service/base/string_split.h b/system/service/base/string_split.h new file mode 100644 index 0000000000..ee1a13fa40 --- /dev/null +++ b/system/service/base/string_split.h @@ -0,0 +1,25 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include <string> +#include <vector> + +namespace base { + +std::vector<std::string> SplitString(const std::string& input, char delimiter); + +} // namespace base diff --git a/system/service/core_stack.cpp b/system/service/core_stack.cpp new file mode 100644 index 0000000000..d40d64bd50 --- /dev/null +++ b/system/service/core_stack.cpp @@ -0,0 +1,287 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "core_stack.h" + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +#include <condition_variable> +#include <mutex> +#include <string> + +#define LOG_TAG "BluetoothBase" +#include "osi/include/log.h" + +#include "hardware/bluetooth.h" +#include "hardware/hardware.h" +#include "logging_helpers.h" +#include "osi/include/osi.h" + +namespace { + +std::mutex mutex; +std::condition_variable synchronize; +bool instantiated = false; + +void AdapterStateChangedCallback(bt_state_t state) { + LOG_INFO("Bluetooth state:%s", BtStateText(state)); + if (state == BT_STATE_ON) + synchronize.notify_one(); +} + +void CallbackThreadCallback(bt_cb_thread_evt evt) { + LOG_INFO("%s: %s", __func__, BtEventText(evt)); +} + +// TODO(icoolidge): Audit how these os callouts should be +// implemented (or nulled) for systems w/out wakelocks. +bool SetWakeAlarmCallback(uint64_t delay_millis, + UNUSED_ATTR bool should_wake, + alarm_cb cb, + void *data) { + static timer_t timer; + static bool timer_created; + + if (!timer_created) { + struct sigevent sigevent; + memset(&sigevent, 0, sizeof(sigevent)); + sigevent.sigev_notify = SIGEV_THREAD; + sigevent.sigev_notify_function = (void (*)(union sigval))cb; + sigevent.sigev_value.sival_ptr = data; + timer_create(CLOCK_MONOTONIC, &sigevent, &timer); + timer_created = true; + } + + struct itimerspec new_value; + new_value.it_value.tv_sec = delay_millis / 1000; + new_value.it_value.tv_nsec = (delay_millis % 1000) * 1000 * 1000; + new_value.it_interval.tv_sec = 0; + new_value.it_interval.tv_nsec = 0; + timer_settime(timer, 0, &new_value, nullptr); + + return true; +} + +// Dummy implementation due to no wakelocks. +int AcquireWakeLock(UNUSED_ATTR const char *lock_name) { + return BT_STATUS_SUCCESS; +} + +// Dummy implementation due to no wakelocks. +int ReleaseWakeLock(UNUSED_ATTR const char *lock_name) { + return BT_STATUS_SUCCESS; +} + +void GenericDevicePropertiesCallback(bt_status_t status, + bt_bdaddr_t *remote_address, + int num_properties, + bt_property_t *properties) { + if (status != BT_STATUS_SUCCESS) { + LOG_ERROR("%s: %s", __func__, BtStatusText(status)); + return; + } + + if (!remote_address) { + LOG_INFO("Local adapter properties:"); + } + + for (int i = 0; i < num_properties; ++i) { + bt_property_t *prop = &properties[i]; + switch (prop->type) { + case BT_PROPERTY_BDADDR: { + std::string text = + BtAddrString(reinterpret_cast<bt_bdaddr_t *>(prop->val)); + LOG_INFO("%s: %s", BtPropertyText(prop->type), text.c_str()); + break; + } + case BT_PROPERTY_ADAPTER_SCAN_MODE: { + bt_scan_mode_t *mode = reinterpret_cast<bt_scan_mode_t *>(prop->val); + LOG_INFO("%s: %s", BtPropertyText(prop->type), BtScanModeText(*mode)); + std::lock_guard<std::mutex> lock(mutex); + synchronize.notify_one(); + break; + } + case BT_PROPERTY_BDNAME: { + bt_bdname_t *name = reinterpret_cast<bt_bdname_t *>(prop->val); + LOG_INFO("%s: %s", BtPropertyText(prop->type), + reinterpret_cast<char *>(name->name)); + std::lock_guard<std::mutex> lock(mutex); + synchronize.notify_one(); + break; + } + default: + LOG_INFO("%s: %s", __func__, BtPropertyText(prop->type)); + break; + } + } +} + +void AclStateChangedCallback(bt_status_t status, bt_bdaddr_t *remote_bd_addr, + bt_acl_state_t state) { + if (status != BT_STATUS_SUCCESS) { + LOG_ERROR("%s: %s", __func__, BtStatusText(status)); + return; + } + + std::string text = BtAddrString(remote_bd_addr); + LOG_INFO("%s: %s: %s", __func__, text.c_str(), BtAclText(state)); +} + +void LocalAdapterPropertiesCallback(bt_status_t status, int num_properties, + bt_property_t *properties) { + GenericDevicePropertiesCallback(status, nullptr, num_properties, properties); +} + +bt_callbacks_t bt_callbacks = { + sizeof(bt_callbacks_t), + AdapterStateChangedCallback, + LocalAdapterPropertiesCallback, + GenericDevicePropertiesCallback, + nullptr, /* device_found_cb */ + nullptr, /* discovery_state_changed_cb */ + nullptr, /* pin_request_cb */ + nullptr, /* ssp_request_cb */ + nullptr, /* bond_state_changed_cb */ + AclStateChangedCallback, + CallbackThreadCallback, + nullptr, /* dut_mode_recv_cb */ + nullptr, /* le_test_mode_cb */ + nullptr /* energy_info_cb */ +}; + +bt_os_callouts_t callouts = { + sizeof(bt_os_callouts_t), + SetWakeAlarmCallback, + AcquireWakeLock, + ReleaseWakeLock +}; + +} // namespace + +namespace bluetooth { + +CoreStack::CoreStack() : adapter_(nullptr), hal_(nullptr) { + std::lock_guard<std::mutex> lock(mutex); + // TODO(icoolidge): DCHECK(!instantiated); + instantiated = true; +} + +bool CoreStack::Initialize() { + std::unique_lock<std::mutex> lock(mutex); + + // Load the bluetooth module. + const hw_module_t *module; + int status = hw_get_module(BT_HARDWARE_MODULE_ID, &module); + if (status) { + LOG_ERROR("Error getting bluetooth module"); + return false; + } + + // Open the bluetooth device. + hw_device_t *device; + status = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device); + if (status) { + LOG_ERROR("Error opening bluetooth module"); + return false; + } + + // TODO(icoolidge): Audit initialization and teardown. + adapter_ = reinterpret_cast<bluetooth_device_t *>(device); + hal_ = adapter_->get_bluetooth_interface(); + + // Bind module callbacks to local handlers. + status = hal_->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + LOG_ERROR("Error binding callbacks"); + return false; + } + + status = hal_->set_os_callouts(&callouts); + if (status != BT_STATUS_SUCCESS) { + LOG_ERROR("Error binding OS callbacks"); + return false; + } + + status = hal_->enable(); + if (status) { + LOG_ERROR("Enable failed: %d", status); + return false; + } + + synchronize.wait(lock); + LOG_INFO("CoreStack::Initialize success"); + return true; +} + +bool CoreStack::SetAdapterName(const std::string &name) { + bt_bdname_t n; + snprintf(reinterpret_cast<char *>(n.name), sizeof(n.name), "%s", + name.c_str()); + bt_property_t prop; + prop.len = sizeof(n); + prop.val = &n; + prop.type = BT_PROPERTY_BDNAME; + + std::unique_lock<std::mutex> lock(mutex); + + int status = hal_->set_adapter_property(&prop); + if (status) { + LOG_ERROR("%s: prop change failed: %d", __func__, status); + return false; + } + + synchronize.wait(lock); + return true; +} + +bool CoreStack::SetClassicDiscoverable() { + bt_scan_mode_t mode = BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE; + bt_property_t disc; + disc.len = sizeof(mode); + disc.val = &mode; + disc.type = BT_PROPERTY_ADAPTER_SCAN_MODE; + + std::unique_lock<std::mutex> lock(mutex); + + int status = hal_->set_adapter_property(&disc); + if (status) { + LOG_ERROR("Prop change failed: %d", status); + return false; + } + + synchronize.wait(lock); + return true; +} + +const void *CoreStack::GetInterface(const char *profile) { + std::unique_lock<std::mutex> lock(mutex); + // Get the interface to the GATT profile. + const void *interface = hal_->get_profile_interface(profile); + if (!interface) { + LOG_ERROR("Error getting %s interface", profile); + return nullptr; + } + return interface; +} + +CoreStack::~CoreStack() { + // TODO(icoolidge): Disable bluetooth hardware, clean up library state. + std::lock_guard<std::mutex> lock(mutex); + instantiated = false; +} + +} // namespace bluetooth diff --git a/system/service/core_stack.h b/system/service/core_stack.h new file mode 100644 index 0000000000..8b5671af66 --- /dev/null +++ b/system/service/core_stack.h @@ -0,0 +1,57 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include <string> + +#include "hardware/bluetooth.h" + +namespace bluetooth { + +// This represents the core Bluetooth stack, +// with high level operations that affect many profiles. +// It is also used to access profile interfaces. +class CoreStack { + public: + CoreStack(); + ~CoreStack(); + + // Initialize the bluetooth stack and device. + bool Initialize(); + + // Set the device name. + // This can be referenced in BLE GAP advertisements. + bool SetAdapterName(const std::string& name); + + // Allow activated classic profiles to be discovered. + bool SetClassicDiscoverable(); + + // Get an interface for a profile (BLE GATT, A2DP, etc). + const void *GetInterface(const char* profile); + + private: + // Prevent copy and assignment. + CoreStack& operator=(const CoreStack& rhs) = delete; + CoreStack(const CoreStack& rhs) = delete; + + // Our libhardware handle. + bluetooth_device_t *adapter_; + + // Common bluetooth interface handle. + const bt_interface_t *hal_; +}; + +} // namespace bluetooth diff --git a/system/service/gatt_server.cpp b/system/service/gatt_server.cpp new file mode 100644 index 0000000000..54e407ec92 --- /dev/null +++ b/system/service/gatt_server.cpp @@ -0,0 +1,704 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "gatt_server.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <algorithm> +#include <array> +#include <condition_variable> +#include <map> +#include <memory> +#include <mutex> +#include <set> +#include <string> +#include <unordered_map> +#include <vector> + +#define LOG_TAG "GattServer" +#include "osi/include/log.h" + +#include "core_stack.h" +#include "hardware/bluetooth.h" +#include "hardware/bt_gatt.h" +#include "logging_helpers.h" +#include "osi/include/osi.h" +#include "uuid.h" + +namespace { + +const size_t kMaxGattAttributeSize = 512; +// TODO(icoolidge): Difficult to generalize without knowing how many attributes. +const int kNumBlueDroidHandles = 60; + +// TODO(icoolidge): Support multiple instances +static bluetooth::gatt::ServerInternals *internal = nullptr; + +enum { kPipeReadEnd = 0, kPipeWriteEnd = 1, kPipeNumEnds = 2 }; + +} // namespace + +namespace bluetooth { +namespace gatt { + +struct Characteristic { + Uuid uuid; + int blob_section; + std::vector<uint8_t> blob; + + // Support synchronized blob updates by latching under mutex. + std::vector<uint8_t> next_blob; + bool next_blob_pending; + bool notify; +}; + +struct ServerInternals { + ServerInternals(); + ~ServerInternals(); + int Initialize(CoreStack *bt); + + // This maps API attribute UUIDs to BlueDroid handles. + std::map<Uuid, int> uuid_to_attribute; + + // The attribute cache, indexed by BlueDroid handles. + std::unordered_map<int, Characteristic> characteristics; + + // Associate a control attribute with its value attribute. + std::unordered_map<int, int> controlled_blobs; + + ScanResults scan_results; + + Uuid last_write; + const btgatt_interface_t *gatt; + int server_if; + int client_if; + int service_handle; + btgatt_srvc_id_t service_id; + std::set<int> connections; + + std::mutex lock; + std::condition_variable api_synchronize; + int pipefd[kPipeNumEnds]; +}; + +} // namespace gatt +} // namespace bluetooth + +namespace { + +/** Callback invoked in response to register_server */ +void RegisterServerCallback(int status, int server_if, bt_uuid_t *app_uuid) { + LOG_INFO("%s: status:%d server_if:%d app_uuid:%p", __func__, status, + server_if, app_uuid); + + internal->server_if = server_if; + + btgatt_srvc_id_t service_id; + service_id.id.uuid = *app_uuid; + service_id.id.inst_id = 0; + service_id.is_primary = true; + + bt_status_t btstat = internal->gatt->server->add_service( + server_if, &service_id, kNumBlueDroidHandles); +} + +void ServiceAddedCallback(int status, int server_if, btgatt_srvc_id_t *srvc_id, + int srvc_handle) { + LOG_INFO("%s: status:%d server_if:%d gatt_srvc_id:%u srvc_handle:%d", + __func__, status, server_if, srvc_id->id.inst_id, srvc_handle); + + std::lock_guard<std::mutex> lock(internal->lock); + internal->server_if = server_if; + internal->service_handle = srvc_handle; + internal->service_id = *srvc_id; + // This finishes the Initialize call. + internal->api_synchronize.notify_one(); +} + +void RequestReadCallback(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int attribute_offset_octets, + bool is_long) { + std::lock_guard<std::mutex> lock(internal->lock); + + bluetooth::gatt::Characteristic &ch = internal->characteristics[attr_handle]; + + // Latch next_blob to blob on a 'fresh' read. + if (ch.next_blob_pending && attribute_offset_octets == 0 && + ch.blob_section == 0) { + std::swap(ch.blob, ch.next_blob); + ch.next_blob_pending = false; + } + + const size_t blob_offset_octets = + std::min(ch.blob.size(), ch.blob_section * kMaxGattAttributeSize); + const size_t blob_remaining = ch.blob.size() - blob_offset_octets; + const size_t attribute_size = std::min(kMaxGattAttributeSize, blob_remaining); + + std::string addr(BtAddrString(bda)); + LOG_INFO( + "%s: connection:%d (%s) reading attr:%d attribute_offset_octets:%d " + "blob_section:%u (is_long:%u)", + __func__, conn_id, addr.c_str(), attr_handle, attribute_offset_octets, + ch.blob_section, is_long); + + btgatt_response_t response; + response.attr_value.len = 0; + + if (attribute_offset_octets < static_cast<int>(attribute_size)) { + std::copy(ch.blob.begin() + blob_offset_octets + attribute_offset_octets, + ch.blob.begin() + blob_offset_octets + attribute_size, + response.attr_value.value); + response.attr_value.len = attribute_size - attribute_offset_octets; + } + + response.attr_value.handle = attr_handle; + response.attr_value.offset = attribute_offset_octets; + response.attr_value.auth_req = 0; + internal->gatt->server->send_response(conn_id, trans_id, 0, &response); +} + +void RequestWriteCallback(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int attribute_offset, int length, + bool need_rsp, bool is_prep, uint8_t *value) { + std::string addr(BtAddrString(bda)); + LOG_INFO( + "%s: connection:%d (%s:trans:%d) write attr:%d attribute_offset:%d " + "length:%d " + "need_resp:%u is_prep:%u", + __func__, conn_id, addr.c_str(), trans_id, attr_handle, attribute_offset, + length, need_rsp, is_prep); + + std::lock_guard<std::mutex> lock(internal->lock); + + bluetooth::gatt::Characteristic &ch = internal->characteristics[attr_handle]; + + ch.blob.resize(attribute_offset + length); + + std::copy(value, value + length, ch.blob.begin() + attribute_offset); + + auto target_blob = internal->controlled_blobs.find(attr_handle); + // If this is a control attribute, adjust offset of the target blob. + if (target_blob != internal->controlled_blobs.end() && ch.blob.size() == 1u) { + internal->characteristics[target_blob->second].blob_section = ch.blob[0]; + LOG_INFO("%s: updating attribute %d blob_section to %u", __func__, + target_blob->second, ch.blob[0]); + } else if (!is_prep) { + // This is a single frame characteristic write. + // Notify upwards because we're done now. + const bluetooth::Uuid::Uuid128Bit &attr_uuid = ch.uuid.GetFullBigEndian(); + int status = write(internal->pipefd[kPipeWriteEnd], attr_uuid.data(), + attr_uuid.size()); + if (-1 == status) + LOG_ERROR("%s: write failed: %s", __func__, strerror(errno)); + } else { + // This is a multi-frame characteristic write. + // Wait for an 'RequestExecWriteCallback' to notify completion. + internal->last_write = ch.uuid; + } + + // Respond only if needed. + if (!need_rsp) return; + + btgatt_response_t response; + response.attr_value.handle = attr_handle; + response.attr_value.offset = attribute_offset; + response.attr_value.len = length; + response.attr_value.auth_req = 0; + internal->gatt->server->send_response(conn_id, trans_id, 0, &response); +} + +void RequestExecWriteCallback(int conn_id, int trans_id, bt_bdaddr_t *bda, + int exec_write) { + std::string addr(BtAddrString(bda)); + LOG_INFO("%s: connection:%d (%s:trans:%d) exec_write:%d", __func__, conn_id, + addr.c_str(), trans_id, exec_write); + + if (!exec_write) + return; + + std::lock_guard<std::mutex> lock(internal->lock); + // Communicate the attribute UUID as notification of a write update. + const bluetooth::Uuid::Uuid128Bit uuid = + internal->last_write.GetFullBigEndian(); + int status = write(internal->pipefd[kPipeWriteEnd], uuid.data(), uuid.size()); + if (-1 == status) + LOG_ERROR("%s: write failed: %s", __func__, strerror(errno)); +} + +void ConnectionCallback(int conn_id, int server_if, int connected, + bt_bdaddr_t *bda) { + std::string addr(BtAddrString(bda)); + LOG_INFO("%s: connection:%d server_if:%d connected:%d addr:%s", __func__, + conn_id, server_if, connected, addr.c_str()); + if (connected == 1) { + internal->connections.insert(conn_id); + } else if (connected == 0) { + internal->connections.erase(conn_id); + } +} + +void CharacteristicAddedCallback(int status, int server_if, bt_uuid_t *uuid, + int srvc_handle, int char_handle) { + LOG_INFO("%s: status:%d server_if:%d service_handle:%d char_handle:%d", + __func__, status, server_if, srvc_handle, char_handle); + + bluetooth::Uuid id(*uuid); + + std::lock_guard<std::mutex> lock(internal->lock); + + internal->uuid_to_attribute[id] = char_handle; + internal->characteristics[char_handle].uuid = id; + internal->characteristics[char_handle].blob_section = 0; + + // This terminates an AddCharacteristic. + internal->api_synchronize.notify_one(); +} + +void DescriptorAddedCallback(int status, int server_if, bt_uuid_t *uuid, + int srvc_handle, int descr_handle) { + LOG_INFO( + "%s: status:%d server_if:%d service_handle:%d uuid[0]:%u " + "descr_handle:%d", + __func__, status, server_if, srvc_handle, uuid->uu[0], descr_handle); +} + +void ServiceStartedCallback(int status, int server_if, int srvc_handle) { + LOG_INFO("%s: status:%d server_if:%d srvc_handle:%d", __func__, status, + server_if, srvc_handle); + + // The UUID provided here is unimportant, and is only used to satisfy + // BlueDroid. + // It must be different than any other registered UUID. + bt_uuid_t client_id = internal->service_id.id.uuid; + ++client_id.uu[15]; + + bt_status_t btstat = internal->gatt->client->register_client(&client_id); + if (btstat != BT_STATUS_SUCCESS) { + LOG_ERROR("%s: Failed to register client", __func__); + } +} + +void RegisterClientCallback(int status, int client_if, bt_uuid_t *app_uuid) { + LOG_INFO("%s: status:%d client_if:%d uuid[0]:%u", __func__, status, + client_if, app_uuid->uu[0]); + internal->client_if = client_if; + + // Setup our advertisement. This has no callback. + bt_status_t btstat = internal->gatt->client->set_adv_data( + client_if, false, /* beacon, not scan response */ + false, /* name */ + false, /* no txpower */ + 2, 2, /* interval */ + 0, /* appearance */ + 0, nullptr, /* no mfg data */ + 0, nullptr, /* no service data */ + 0, nullptr /* no service id yet */); + if (btstat != BT_STATUS_SUCCESS) { + LOG_ERROR("Failed to set advertising data"); + return; + } + + // TODO(icoolidge): Deprecated, use multi-adv interface. + // This calls back to ListenCallback. + btstat = internal->gatt->client->listen(client_if, true); + if (btstat != BT_STATUS_SUCCESS) { + LOG_ERROR("Failed to start listening"); + } +} + +void ListenCallback(int status, int client_if) { + LOG_INFO("%s: status:%d client_if:%d", __func__, status, client_if); + // This terminates a Start call. + std::lock_guard<std::mutex> lock(internal->lock); + internal->api_synchronize.notify_one(); +} + +void ServiceStoppedCallback(int status, int server_if, int srvc_handle) { + LOG_INFO("%s: status:%d server_if:%d srvc_handle:%d", __func__, status, + server_if, srvc_handle); + // This terminates a Stop call. + // TODO(icoolidge): make this symmetric with start + std::lock_guard<std::mutex> lock(internal->lock); + internal->api_synchronize.notify_one(); +} + +void ScanResultCallback(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data) { + std::string addr(BtAddrString(bda)); + (void)adv_data; + std::lock_guard<std::mutex> lock(internal->lock); + internal->scan_results[addr] = rssi; +} + +void ClientConnectCallback(int conn_id, int status, int client_if, + bt_bdaddr_t *bda) { + std::string addr(BtAddrString(bda)); + LOG_INFO("%s: conn_id:%d status:%d client_if:%d %s", __func__, conn_id, + status, client_if, addr.c_str()); +} + +void ClientDisconnectCallback(int conn_id, int status, int client_if, + bt_bdaddr_t *bda) { + std::string addr(BtAddrString(bda)); + LOG_INFO("%s: conn_id:%d status:%d client_if:%d %s", __func__, conn_id, + status, client_if, addr.c_str()); +} + +void IndicationSentCallback(UNUSED_ATTR int conn_id, + UNUSED_ATTR int status) { + // TODO(icoolidge): what to do +} + +void ResponseConfirmationCallback(UNUSED_ATTR int status, + UNUSED_ATTR int handle) { + // TODO(icoolidge): what to do +} + +const btgatt_server_callbacks_t gatt_server_callbacks = { + RegisterServerCallback, + ConnectionCallback, + ServiceAddedCallback, + nullptr, /* included_service_added_cb */ + CharacteristicAddedCallback, + DescriptorAddedCallback, + ServiceStartedCallback, + ServiceStoppedCallback, + nullptr, /* service_deleted_cb */ + RequestReadCallback, + RequestWriteCallback, + RequestExecWriteCallback, + ResponseConfirmationCallback, + IndicationSentCallback, + nullptr, /* congestion_cb*/ + nullptr, /* mtu_changed_cb */ +}; + +// TODO(eisenbach): Refactor GATT interface to not require servers +// to refer to the client interface. +const btgatt_client_callbacks_t gatt_client_callbacks = { + RegisterClientCallback, + ScanResultCallback, + ClientConnectCallback, + ClientDisconnectCallback, + nullptr, /* search_complete_cb; */ + nullptr, /* search_result_cb; */ + nullptr, /* get_characteristic_cb; */ + nullptr, /* get_descriptor_cb; */ + nullptr, /* get_included_service_cb; */ + nullptr, /* register_for_notification_cb; */ + nullptr, /* notify_cb; */ + nullptr, /* read_characteristic_cb; */ + nullptr, /* write_characteristic_cb; */ + nullptr, /* read_descriptor_cb; */ + nullptr, /* write_descriptor_cb; */ + nullptr, /* execute_write_cb; */ + nullptr, /* read_remote_rssi_cb; */ + ListenCallback, + nullptr, /* configure_mtu_cb; */ + nullptr, /* scan_filter_cfg_cb; */ + nullptr, /* scan_filter_param_cb; */ + nullptr, /* scan_filter_status_cb; */ + nullptr, /* multi_adv_enable_cb */ + nullptr, /* multi_adv_update_cb; */ + nullptr, /* multi_adv_data_cb*/ + nullptr, /* multi_adv_disable_cb; */ + nullptr, /* congestion_cb; */ + nullptr, /* batchscan_cfg_storage_cb; */ + nullptr, /* batchscan_enb_disable_cb; */ + nullptr, /* batchscan_reports_cb; */ + nullptr, /* batchscan_threshold_cb; */ + nullptr, /* track_adv_event_cb; */ +}; + +const btgatt_callbacks_t gatt_callbacks = { + /** Set to sizeof(btgatt_callbacks_t) */ + sizeof(btgatt_callbacks_t), + + /** GATT Client callbacks */ + &gatt_client_callbacks, + + /** GATT Server callbacks */ + &gatt_server_callbacks}; + +} // namespace + +namespace bluetooth { +namespace gatt { + +int ServerInternals::Initialize(CoreStack *bt) { + // Get the interface to the GATT profile. + gatt = reinterpret_cast<const btgatt_interface_t *>( + bt->GetInterface(BT_PROFILE_GATT_ID)); + if (!gatt) { + LOG_ERROR("Error getting GATT interface"); + return -1; + } + + bt_status_t btstat = gatt->init(&gatt_callbacks); + if (btstat != BT_STATUS_SUCCESS) { + LOG_ERROR("Failed to initialize gatt interface"); + return -1; + } + + int status = pipe(pipefd); + if (status == -1) { + LOG_ERROR("pipe creation failed: %s", strerror(errno)); + return -1; + } + + return 0; +} + +ServerInternals::ServerInternals() + : gatt(nullptr), + server_if(0), + client_if(0), + service_handle(0), + pipefd{INVALID_FD, INVALID_FD} {} + +ServerInternals::~ServerInternals() { + if (pipefd[0] != INVALID_FD) + close(pipefd[0]); + if (pipefd[1] != INVALID_FD) + close(pipefd[1]); +} + +Server::Server() : internal_(nullptr) {} + +Server::~Server() {} + +bool Server::Initialize(const Uuid &service_id, int *gatt_pipe, CoreStack *bt) { + internal_.reset(new ServerInternals); + if (!internal_) { + LOG_ERROR("Error creating internals"); + return false; + } + internal = internal_.get(); + + std::unique_lock<std::mutex> lock(internal_->lock); + int status = internal_->Initialize(bt); + if (status) { + LOG_ERROR("Error initializing internals"); + return false; + } + + bt_uuid_t uuid = service_id.GetBlueDroid(); + + bt_status_t btstat = internal_->gatt->server->register_server(&uuid); + if (btstat != BT_STATUS_SUCCESS) { + LOG_ERROR("Failed to register server"); + return false; + } + + internal_->api_synchronize.wait(lock); + // TODO(icoolidge): Better error handling. + if (internal_->server_if == 0) { + LOG_ERROR("Initialization of server failed"); + return false; + } + + *gatt_pipe = internal_->pipefd[kPipeReadEnd]; + LOG_INFO("Server Initialize succeeded"); + return true; +} + +bool Server::SetAdvertisement(const std::vector<Uuid> &ids, + const std::vector<uint8_t> &service_data, + bool transmit_name) { + std::vector<uint8_t> id_data; + auto mutable_service_data = service_data; + + for (const Uuid &id : ids) { + const auto le_id = id.GetFullLittleEndian(); + id_data.insert(id_data.end(), le_id.begin(), le_id.end()); + } + + std::lock_guard<std::mutex> lock(internal_->lock); + + // Setup our advertisement. This has no callback. + bt_status_t btstat = internal->gatt->client->set_adv_data( + internal_->client_if, false, /* beacon, not scan response */ + transmit_name, /* name */ + false, /* no txpower */ + 2, 2, /* interval */ + 0, /* appearance */ + 0, nullptr, /* no mfg data */ + mutable_service_data.size(), + reinterpret_cast<char *>(mutable_service_data.data()), id_data.size(), + reinterpret_cast<char *>(id_data.data())); + if (btstat != BT_STATUS_SUCCESS) { + LOG_ERROR("Failed to set advertising data"); + return false; + } + return true; +} + +bool Server::SetScanResponse(const std::vector<Uuid> &ids, + const std::vector<uint8_t> &service_data, + bool transmit_name) { + std::vector<uint8_t> id_data; + auto mutable_service_data = service_data; + + for (const Uuid &id : ids) { + const auto le_id = id.GetFullLittleEndian(); + id_data.insert(id_data.end(), le_id.begin(), le_id.end()); + } + + std::lock_guard<std::mutex> lock(internal_->lock); + + // Setup our advertisement. This has no callback. + bt_status_t btstat = internal->gatt->client->set_adv_data( + internal_->client_if, true, /* scan response */ + transmit_name, /* name */ + false, /* no txpower */ + 2, 2, /* interval */ + 0, /* appearance */ + 0, nullptr, /* no mfg data */ + mutable_service_data.size(), + reinterpret_cast<char *>(mutable_service_data.data()), id_data.size(), + reinterpret_cast<char *>(id_data.data())); + if (btstat != BT_STATUS_SUCCESS) { + LOG_ERROR("Failed to set scan response data"); + return false; + } + return true; +} + +bool Server::AddCharacteristic(const Uuid &id, int properties, int permissions) { + bt_uuid_t char_id = id.GetBlueDroid(); + + std::unique_lock<std::mutex> lock(internal->lock); + bt_status_t btstat = internal->gatt->server->add_characteristic( + internal->server_if, internal->service_handle, &char_id, properties, + permissions); + internal_->api_synchronize.wait(lock); + const int handle = internal->uuid_to_attribute[id]; + internal->characteristics[handle].notify = properties & kPropertyNotify; + return true; +} + +bool Server::AddBlob(const Uuid &id, const Uuid &control_id, int properties, + int permissions) { + bt_uuid_t char_id = id.GetBlueDroid(); + bt_uuid_t ctrl_id = control_id.GetBlueDroid(); + + std::unique_lock<std::mutex> lock(internal->lock); + + // First, add the primary attribute (characteristic value) + bt_status_t btstat = internal->gatt->server->add_characteristic( + internal->server_if, internal->service_handle, &char_id, properties, + permissions); + internal->api_synchronize.wait(lock); + + // Next, add the secondary attribute (blob control). + // Control attributes have fixed permissions/properties. + const int kControlPermissions = kPermissionRead | kPermissionWrite; + const int kControlProperties = kPropertyRead | kPropertyWrite; + + btstat = internal->gatt->server->add_characteristic( + internal->server_if, internal->service_handle, &ctrl_id, + kControlProperties, kControlPermissions); + internal->api_synchronize.wait(lock); + + // Finally, associate the control attribute with the value attribute. + // Also, initialize the control attribute to a readable zero. + const int control_attribute = internal->uuid_to_attribute[control_id]; + const int blob_attribute = internal->uuid_to_attribute[id]; + internal->controlled_blobs[control_attribute] = blob_attribute; + internal->characteristics[blob_attribute].notify = properties & kPropertyNotify; + + Characteristic &ctrl = internal->characteristics[control_attribute]; + ctrl.next_blob.clear(); + ctrl.next_blob.push_back(0); + ctrl.next_blob_pending = true; + ctrl.blob_section = 0; + ctrl.notify = false; + return true; +} + +bool Server::Start() { + std::unique_lock<std::mutex> lock(internal_->lock); + bt_status_t btstat = internal_->gatt->server->start_service( + internal_->server_if, internal_->service_handle, GATT_TRANSPORT_LE); + internal_->api_synchronize.wait(lock); + return true; +} + +bool Server::Stop() { + std::unique_lock<std::mutex> lock(internal_->lock); + bt_status_t btstat = internal_->gatt->server->stop_service( + internal_->server_if, internal_->service_handle); + internal_->api_synchronize.wait(lock); + return true; +} + +bool Server::ScanEnable() { + bt_status_t btstat = internal_->gatt->client->scan(true); + if (btstat) { + LOG_ERROR("Enable scan failed: %d", btstat); + return false; + } + return true; +} + +bool Server::ScanDisable() { + bt_status_t btstat = internal_->gatt->client->scan(false); + if (btstat) { + LOG_ERROR("Disable scan failed: %d", btstat); + return false; + } + return true; +} + +bool Server::GetScanResults(ScanResults *results) { + std::lock_guard<std::mutex> lock(internal_->lock); + *results = internal_->scan_results; + return true; +} + +bool Server::SetCharacteristicValue(const Uuid &id, + const std::vector<uint8_t> &value) { + std::lock_guard<std::mutex> lock(internal->lock); + const int attribute_id = internal->uuid_to_attribute[id]; + Characteristic &ch = internal->characteristics[attribute_id]; + ch.next_blob = value; + ch.next_blob_pending = true; + + if (!ch.notify) + return true; + + for (auto connection : internal->connections) { + char dummy = 0; + internal_->gatt->server->send_indication(internal->server_if, + attribute_id, + connection, + sizeof(dummy), + true, + &dummy); + } + return true; +} + +bool Server::GetCharacteristicValue(const Uuid &id, std::vector<uint8_t> *value) { + std::lock_guard<std::mutex> lock(internal_->lock); + const int attribute_id = internal_->uuid_to_attribute[id]; + *value = internal_->characteristics[attribute_id].blob; + return true; +} + +} // namespace gatt +} // namespace bluetooth diff --git a/system/service/gatt_server.h b/system/service/gatt_server.h new file mode 100644 index 0000000000..2328395b29 --- /dev/null +++ b/system/service/gatt_server.h @@ -0,0 +1,117 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include <array> +#include <memory> +#include <unordered_map> +#include <vector> + +#include "hardware/bluetooth.h" +#include "hardware/bt_gatt.h" +#include "uuid.h" + +namespace bluetooth { + +class CoreStack; + +namespace gatt { + +// Attribute permission values +const int kPermissionRead = 0x1; +const int kPermissionReadEncrypted = 0x2; +const int kPermissionReadEncryptedMitm = 0x4; +const int kPermissionWrite = 0x10; +const int kPermissionWriteEnecrypted = 0x20; +const int KPermissionWriteEncryptedMitm = 0x40; +const int kPermissionWriteSigned = 0x80; +const int kPermissionWriteSignedMitm = 0x100; + +// GATT characteristic properties bit-field values +const int kPropertyBroadcast = 0x1; +const int kPropertyRead = 0x2; +const int kPropertyWriteNoResponse = 0x4; +const int kPropertyWrite = 0x8; +const int kPropertyNotify = 0x10; +const int kPropertyIndicate = 0x20; +const int kPropertySignedWrite = 0x40; +const int kPropertyExtendedProps = 0x80; + +// A mapping from string bluetooth addresses to RSSI measurements. +typedef std::unordered_map<std::string, int> ScanResults; + +class ServerInternals; + +// Server is threadsafe and internally locked. +// Asynchronous IO is identified via a gatt_pipe FD, +// and synchronously read with 'GetCharacteristicValue' +class Server { + public: + Server(); + ~Server(); + + // Register GATT interface, initialize internal state, + // and open a pipe for characteristic write notification. + bool Initialize(const Uuid &service_id, int *gatt_pipe, CoreStack *bt); + + // Control the content of service advertisement. + bool SetAdvertisement(const std::vector<Uuid> &ids, + const std::vector<uint8_t> &service_data, + bool transmit_name); + + // Control the content of service scan response. + bool SetScanResponse(const std::vector<Uuid> &ids, + const std::vector<uint8_t> &service_data, + bool transmit_name); + + // Add an ordinary characteristic for reading and/or writing. + bool AddCharacteristic(const Uuid &id, int properties, int permissions); + + // Add a special 'blob' characteristic with a corresponding control + // attribute to manipulate which part of the blob the attribute represents. + bool AddBlob(const Uuid &id, const Uuid &control_id, int properties, + int permissions); + + // Put a new value into a characeteristic. + // It will be read from a client starting at the next 0-offset read. + bool SetCharacteristicValue(const Uuid &id, const std::vector<uint8_t> &value); + + // Get the current value of a characteristic. + bool GetCharacteristicValue(const Uuid &id, std::vector<uint8_t> *value); + + // Start this service. Activate advertisements, allow connections. + // Characteristics should all be created before this. + bool Start(); + + // Cease advertisements and disallow connections. + bool Stop(); + + // Enable LE scan. Scan results will be cached internally. + bool ScanEnable(); + + // Disable LE scan. + bool ScanDisable(); + + // Copy out the cached scan results. + bool GetScanResults(ScanResults *results); + + private: + // Internal data. + std::unique_ptr<ServerInternals> internal_; +}; + +} // namespace gatt +} // namespace bluetooth diff --git a/system/service/host.cpp b/system/service/host.cpp new file mode 100644 index 0000000000..fb38f18027 --- /dev/null +++ b/system/service/host.cpp @@ -0,0 +1,313 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "host.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#include <algorithm> + +#define LOG_TAG "BluetoothHost" +#include "osi/include/log.h" + +#include "base/base64.h" +#include "base/string_number_conversions.h" +#include "base/string_split.h" +#include "core_stack.h" +#include "gatt_server.h" +#include "uuid.h" + +namespace { + +// IPC API is according to: +// https://docs.google.com/document/d/1eRnku-jAyVU1wGJsLT2CzWi0-8bs2g49s1b3FR_GApM +const char kSetAdapterNameCommand[] = "set-device-name"; +const char kCreateServiceCommand[] = "create-service"; +const char kDestroyServiceCommand[] = "destroy-service"; +const char kAddCharacteristicCommand[] = "add-characteristic"; +const char kSetCharacteristicValueCommand[] = "set-characteristic-value"; +const char kSetAdvertisementCommand[] = "set-advertisement"; +const char kSetScanResponseCommand[] = "set-scan-response"; +const char kStartServiceCommand[] = "start-service"; +const char kStopServiceCommand[] = "stop-service"; +const char kWriteCharacteristicCommand[] = "write-characteristic"; + +// Useful values for indexing Host::pfds_ +// Not super general considering that we should be able to support +// many GATT FDs owned by one Host. +enum { + kFdIpc = 0, + kFdGatt = 1, + kPossibleFds = 2, +}; + +bool TokenBool(const std::string& text) { + return text == "true"; +} + +} // namespace + +namespace bluetooth { + +Host::Host(int sockfd, CoreStack* bt) + : bt_(bt), pfds_(1, {sockfd, POLLIN, 0}) {} + +Host::~Host() { + close(pfds_[0].fd); +} + +bool Host::EventLoop() { + while (true) { + int status = + TEMP_FAILURE_RETRY(ppoll(pfds_.data(), pfds_.size(), nullptr, nullptr)); + if (status < 1) { + LOG_ERROR("ppoll error"); + return false; + } + + if (pfds_[kFdIpc].revents && !OnMessage()) { + return false; + } + + if (pfds_.size() == kPossibleFds && + pfds_[kFdGatt].revents && + !OnGattWrite()) { + return false; + } + } + return true; +} + +bool Host::OnSetAdapterName(const std::string& name) { + std::string decoded_data; + base::Base64Decode(name, &decoded_data); + return bt_->SetAdapterName(decoded_data); +} + +bool Host::OnCreateService(const std::string& service_uuid) { + gatt_servers_[service_uuid] = std::unique_ptr<gatt::Server>(new gatt::Server); + + int gattfd; + bool status = + gatt_servers_[service_uuid]->Initialize(Uuid(service_uuid), &gattfd, bt_); + if (!status) { + LOG_ERROR("Failed to initialize bluetooth"); + return false; + } + pfds_.resize(kPossibleFds); + pfds_[kFdGatt] = {gattfd, POLLIN, 0}; + return true; +} + +bool Host::OnDestroyService(const std::string& service_uuid) { + gatt_servers_.erase(service_uuid); + close(pfds_[1].fd); + pfds_.resize(1); + return true; +} + +bool Host::OnAddCharacteristic(const std::string& service_uuid, + const std::string& characteristic_uuid, + const std::string& control_uuid, + const std::string& options) { + const std::vector<std::string> option_tokens( + base::SplitString(options, '.')); + + int properties_mask = 0; + int permissions_mask = 0; + + if (std::find(option_tokens.begin(), option_tokens.end(), "notify") != + option_tokens.end()) { + permissions_mask |= gatt::kPermissionRead; + properties_mask |= gatt::kPropertyRead; + properties_mask |= gatt::kPropertyNotify; + } + if (std::find(option_tokens.begin(), option_tokens.end(), "read") != + option_tokens.end()) { + permissions_mask |= gatt::kPermissionRead; + properties_mask |= gatt::kPropertyRead; + } + if (std::find(option_tokens.begin(), option_tokens.end(), "write") != + option_tokens.end()) { + permissions_mask |= gatt::kPermissionWrite; + properties_mask |= gatt::kPropertyWrite; + } + + if (control_uuid.empty()) { + gatt_servers_[service_uuid]->AddCharacteristic( + Uuid(characteristic_uuid), properties_mask, permissions_mask); + } else { + gatt_servers_[service_uuid]->AddBlob(Uuid(characteristic_uuid), + Uuid(control_uuid), properties_mask, + permissions_mask); + } + return true; +} + +bool Host::OnSetCharacteristicValue(const std::string& service_uuid, + const std::string& characteristic_uuid, + const std::string& value) { + std::string decoded_data; + base::Base64Decode(value, &decoded_data); + std::vector<uint8_t> blob_data(decoded_data.begin(), decoded_data.end()); + gatt_servers_[service_uuid]->SetCharacteristicValue(Uuid(characteristic_uuid), + blob_data); + return true; +} + +bool Host::OnSetAdvertisement(const std::string& service_uuid, + const std::string& advertise_uuids, + const std::string& advertise_data, + const std::string& transmit_name) { + LOG_INFO("%s: service:%s uuids:%s data:%s", __func__, service_uuid.c_str(), + advertise_uuids.c_str(), advertise_data.c_str()); + + const std::vector<std::string> advertise_uuid_tokens( + base::SplitString(advertise_uuids, '.')); + + // string -> vector<Uuid> + std::vector<Uuid> ids; + for (const auto& uuid_token : advertise_uuid_tokens) + ids.emplace_back(uuid_token); + + std::string decoded_data; + base::Base64Decode(advertise_data, &decoded_data); + std::vector<uint8_t> blob_data(decoded_data.begin(), decoded_data.end()); + gatt_servers_[service_uuid]->SetAdvertisement(ids, blob_data, + TokenBool(transmit_name)); + return true; +} + +bool Host::OnSetScanResponse(const std::string& service_uuid, + const std::string& scan_response_uuids, + const std::string& scan_response_data, + const std::string& transmit_name) { + const std::vector<std::string> scan_response_uuid_tokens( + base::SplitString(scan_response_uuids, '.')); + + // string -> vector<Uuid> + std::vector<Uuid> ids; + for (const auto& uuid_token : scan_response_uuid_tokens) + ids.emplace_back(uuid_token); + + std::string decoded_data; + base::Base64Decode(scan_response_data, &decoded_data); + std::vector<uint8_t> blob_data(decoded_data.begin(), decoded_data.end()); + gatt_servers_[service_uuid]->SetScanResponse(ids, blob_data, + TokenBool(transmit_name)); + return true; +} + +bool Host::OnStartService(const std::string& service_uuid) { + return gatt_servers_[service_uuid]->Start(); +} + +bool Host::OnStopService(const std::string& service_uuid) { + return gatt_servers_[service_uuid]->Stop(); +} + +bool Host::OnMessage() { + std::string ipc_msg; + int size = recv(pfds_[kFdIpc].fd, &ipc_msg[0], 0, MSG_PEEK | MSG_TRUNC); + if (-1 == size) { + LOG_ERROR("Error reading datagram size: %s", strerror(errno)); + return false; + } else if (0 == size) { + LOG_INFO("%s:%d: Connection closed", __func__, __LINE__); + return false; + } + + ipc_msg.resize(size); + size = read(pfds_[kFdIpc].fd, &ipc_msg[0], ipc_msg.size()); + if (-1 == size) { + LOG_ERROR("Error reading IPC: %s", strerror(errno)); + return false; + } else if (0 == size) { + LOG_INFO("%s:%d: Connection closed", __func__, __LINE__); + return false; + } + + const std::vector<std::string> tokens(base::SplitString(ipc_msg, '|')); + switch (tokens.size()) { + case 2: + if (tokens[0] == kSetAdapterNameCommand) + return OnSetAdapterName(tokens[1]); + if (tokens[0] == kCreateServiceCommand) + return OnCreateService(tokens[1]); + if (tokens[0] == kDestroyServiceCommand) + return OnDestroyService(tokens[1]); + if (tokens[0] == kStartServiceCommand) + return OnStartService(tokens[1]); + if (tokens[0] == kStopServiceCommand) + return OnStopService(tokens[1]); + break; + case 4: + if (tokens[0] == kSetCharacteristicValueCommand) + return OnSetCharacteristicValue(tokens[1], tokens[2], tokens[3]); + break; + case 5: + if (tokens[0] == kSetAdvertisementCommand) + return OnSetAdvertisement(tokens[1], tokens[2], tokens[3], tokens[4]); + if (tokens[0] == kSetScanResponseCommand) + return OnSetScanResponse(tokens[1], tokens[2], tokens[3], tokens[4]); + if (tokens[0] == kAddCharacteristicCommand) + return OnAddCharacteristic(tokens[1], tokens[2], tokens[3], tokens[4]); + break; + default: + break; + } + + LOG_ERROR("Malformed IPC message: %s", ipc_msg.c_str()); + return false; +} + +bool Host::OnGattWrite() { + Uuid::Uuid128Bit id; + int r = read(pfds_[kFdGatt].fd, id.data(), id.size()); + if (r != id.size()) { + LOG_ERROR("Error reading GATT attribute ID"); + return false; + } + + std::vector<uint8_t> value; + // TODO(icoolidge): Generalize this for multiple clients. + auto server = gatt_servers_.begin(); + server->second->GetCharacteristicValue(Uuid(id), &value); + const std::string value_string(value.begin(), value.end()); + std::string encoded_value; + base::Base64Encode(value_string, &encoded_value); + + std::string transmit(kWriteCharacteristicCommand); + transmit += "|" + server->first; + transmit += "|" + base::HexEncode(id.data(), id.size()); + transmit += "|" + encoded_value; + + r = write(pfds_[kFdIpc].fd, transmit.data(), transmit.size()); + if (-1 == r) { + LOG_ERROR("Error replying to IPC: %s", strerror(errno)); + return false; + } + + return true; +} + +} // namespace bluetooth diff --git a/system/service/host.h b/system/service/host.h new file mode 100644 index 0000000000..e695717b38 --- /dev/null +++ b/system/service/host.h @@ -0,0 +1,102 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include <poll.h> + +#include <memory> +#include <string> +#include <unordered_map> + +#include "gatt_server.h" +#include "uuid.h" + +namespace bluetooth { + +class CoreStack; + +// This implements a single threaded event loop which dispatches +// reads from a set of FDs (pfds_) to a set of handlers. +// Reads from the GATT pipe read end will result in a write to +// to the IPC socket, and vise versa. +class Host { + public: + // Host owns the passed sockfd. + Host(int sockfd, CoreStack* bt); + ~Host(); + + // Synchronously handle all events on input FDs. + bool EventLoop(); + + private: + // Handler for IPC message receives. + // Decodes protocol and dispatches to another handler. + bool OnMessage(); + + // Handler for GATT characteristic writes. + // Encodes to protocol and transmits IPC. + bool OnGattWrite(); + + // Applies adapter name changes to stack. + bool OnSetAdapterName(const std::string& name); + + // Handles service creation. + bool OnCreateService(const std::string& service_uuid); + + // Handles service destruction. + bool OnDestroyService(const std::string& service_uuid); + + // Creates a characteristic for a service. + bool OnAddCharacteristic(const std::string& service_uuid, + const std::string& characteristic_uuid, + const std::string& control_uuid, + const std::string& options); + + // Sets the value of a characetistic. + bool OnSetCharacteristicValue(const std::string& service_uuid, + const std::string& characteristic_uuid, + const std::string& value); + + // Applies settings to service advertisement. + bool OnSetAdvertisement(const std::string& service_uuid, + const std::string& advertise_uuids, + const std::string& advertise_data, + const std::string& transmit_name); + + // Applies settings to scan response. + bool OnSetScanResponse(const std::string& service_uuid, + const std::string& advertise_uuids, + const std::string& advertise_data, + const std::string& transmit_name); + + // Starts service (advertisement and connections) + bool OnStartService(const std::string& service_uuid); + + // Stops service. + bool OnStopService(const std::string& service_uuid); + + // weak reference. + CoreStack *bt_; + + // File descripters that we will block against. + std::vector<struct pollfd> pfds_; + + // Container for multiple GATT servers. Currently only one is supported. + // TODO(icoolidge): support many to one for real. + std::unordered_map<std::string, std::unique_ptr<gatt::Server>> gatt_servers_; +}; + +} // namespace bluetooth diff --git a/system/service/logging_helpers.cpp b/system/service/logging_helpers.cpp new file mode 100644 index 0000000000..59cde12719 --- /dev/null +++ b/system/service/logging_helpers.cpp @@ -0,0 +1,135 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "logging_helpers.h" + +#include <string.h> + +#include <string> + +#define CASE_RETURN_TEXT(code) \ + case code: \ + return #code + +const char *BtAvConnectionStateText(const btav_connection_state_t state) { + switch (state) { + CASE_RETURN_TEXT(BTAV_CONNECTION_STATE_DISCONNECTED); + CASE_RETURN_TEXT(BTAV_CONNECTION_STATE_CONNECTING); + CASE_RETURN_TEXT(BTAV_CONNECTION_STATE_CONNECTED); + CASE_RETURN_TEXT(BTAV_CONNECTION_STATE_DISCONNECTING); + default: + return "Invalid AV connection state"; + } +} + +const char *BtAvAudioStateText(const btav_audio_state_t state) { + switch (state) { + CASE_RETURN_TEXT(BTAV_AUDIO_STATE_REMOTE_SUSPEND); + CASE_RETURN_TEXT(BTAV_AUDIO_STATE_STOPPED); + CASE_RETURN_TEXT(BTAV_AUDIO_STATE_STARTED); + default: + return "Invalid audio state"; + } +} + +const char *BtTransportText(const btgatt_transport_t t) { + switch(t) { + CASE_RETURN_TEXT(GATT_TRANSPORT_AUTO); + CASE_RETURN_TEXT(GATT_TRANSPORT_BREDR); + CASE_RETURN_TEXT(GATT_TRANSPORT_LE); + default: + return "unknown transport"; + } +} + +const char *BtStateText(const bt_state_t state) { + switch (state) { + CASE_RETURN_TEXT(BT_STATE_OFF); + CASE_RETURN_TEXT(BT_STATE_ON); + default: + return "unknown state code"; + } +} + +const char *BtScanModeText(const bt_scan_mode_t mode) { + switch (mode) { + CASE_RETURN_TEXT(BT_SCAN_MODE_NONE); + CASE_RETURN_TEXT(BT_SCAN_MODE_CONNECTABLE); + CASE_RETURN_TEXT(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); + default: + return "unknown scan mode"; + } +} + +const char *BtStatusText(const bt_status_t status) { + switch (status) { + CASE_RETURN_TEXT(BT_STATUS_SUCCESS); + CASE_RETURN_TEXT(BT_STATUS_FAIL); + CASE_RETURN_TEXT(BT_STATUS_NOT_READY); + CASE_RETURN_TEXT(BT_STATUS_NOMEM); + CASE_RETURN_TEXT(BT_STATUS_DONE); + CASE_RETURN_TEXT(BT_STATUS_BUSY); + CASE_RETURN_TEXT(BT_STATUS_UNSUPPORTED); + default: + return "unknown status code"; + } +} + +const char *BtPropertyText(const bt_property_type_t prop) { + switch (prop) { + CASE_RETURN_TEXT(BT_PROPERTY_BDNAME); + CASE_RETURN_TEXT(BT_PROPERTY_BDADDR); + CASE_RETURN_TEXT(BT_PROPERTY_UUIDS); + CASE_RETURN_TEXT(BT_PROPERTY_CLASS_OF_DEVICE); + CASE_RETURN_TEXT(BT_PROPERTY_TYPE_OF_DEVICE); + CASE_RETURN_TEXT(BT_PROPERTY_SERVICE_RECORD); + CASE_RETURN_TEXT(BT_PROPERTY_ADAPTER_SCAN_MODE); + CASE_RETURN_TEXT(BT_PROPERTY_ADAPTER_BONDED_DEVICES); + CASE_RETURN_TEXT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT); + CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_FRIENDLY_NAME); + CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_RSSI); + CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_VERSION_INFO); + CASE_RETURN_TEXT(BT_PROPERTY_LOCAL_LE_FEATURES); + CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP); + default: + return "Invalid property"; + } +} + +const char *BtEventText(const bt_cb_thread_evt evt) { + switch (evt) { + CASE_RETURN_TEXT(ASSOCIATE_JVM); + CASE_RETURN_TEXT(DISASSOCIATE_JVM); + default: + return "unknown state code"; + } +} + +const char *BtAclText(const bt_acl_state_t code) { + switch (code) { + CASE_RETURN_TEXT(BT_ACL_STATE_CONNECTED); + CASE_RETURN_TEXT(BT_ACL_STATE_DISCONNECTED); + default: + return "unknown ACL code"; + } +} + +std::string BtAddrString(const bt_bdaddr_t *addr) { + char buffer[20]; + snprintf(buffer, sizeof(buffer), "%02X:%02X:%02X:%02X:%02X:%02X", + addr->address[0], addr->address[1], addr->address[2], + addr->address[3], addr->address[4], addr->address[5]); + return std::string(buffer); +} diff --git a/system/service/logging_helpers.h b/system/service/logging_helpers.h new file mode 100644 index 0000000000..6baafa85b4 --- /dev/null +++ b/system/service/logging_helpers.h @@ -0,0 +1,45 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include <string.h> + +#include <string> + +#include "hardware/bluetooth.h" +#include "hardware/bt_av.h" +#include "hardware/bt_gatt_types.h" + +const char *BtAvConnectionStateText(const btav_connection_state_t state); + +const char *BtAvAudioStateText(const btav_audio_state_t state); + +const char *BtTransportText(const btgatt_transport_t t); + +const char *BtStateText(const bt_state_t state); + +const char *BtScanModeText(const bt_scan_mode_t mode); + +const char *BtStatusText(const bt_status_t status); + +const char *BtPropertyText(const bt_property_type_t prop); + +const char *BtEventText(const bt_cb_thread_evt evt); + +const char *BtAclText(const bt_acl_state_t state); + +// TODO(icoolidge): Address object. +std::string BtAddrString(const bt_bdaddr_t *addr); diff --git a/system/service/main.cpp b/system/service/main.cpp new file mode 100644 index 0000000000..a818f74759 --- /dev/null +++ b/system/service/main.cpp @@ -0,0 +1,77 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#define LOG_TAG "BtHost" +#include "osi/include/log.h" +// For system properties +// TODO(icoolidge): abstraction or non-cutils stub. +#include <cutils/properties.h> +// For init socket environment variable decode +// TODO(icoolidge): abstraction or remove. +#include <cutils/sockets.h> + +#include "core_stack.h" +#include "host.h" + +namespace { + +const char kDisableProperty[] = "persist.bluetooth.disable"; +const char kSocketFromInit[] = "bluetooth"; + +} // namespace + +int main() { + char disable_value[PROPERTY_VALUE_MAX]; + int status = property_get(kDisableProperty, disable_value, nullptr); + if (status && !strcmp(disable_value, "1")) { + LOG_INFO("service disabled"); + return EXIT_SUCCESS; + } + + int server_socket = android_get_control_socket(kSocketFromInit); + if (server_socket == -1) { + LOG_ERROR("failed to get socket from init"); + return EXIT_FAILURE; + } + + status = listen(server_socket, SOMAXCONN); + if (status == -1) { + LOG_ERROR("listen failed: %s", strerror(errno)); + return EXIT_FAILURE; + } + + bluetooth::CoreStack bt; + bt.Initialize(); + + // TODO(icoolidge): accept simultaneous clients + while (true) { + int client_socket = accept4(server_socket, nullptr, nullptr, SOCK_NONBLOCK); + if (status == -1) { + LOG_ERROR("accept failed: %s", strerror(errno)); + return EXIT_FAILURE; + } + + LOG_INFO("client connected: %d", client_socket); + bluetooth::Host bluetooth_host(client_socket, &bt); + bluetooth_host.EventLoop(); + } + + close(server_socket); + return EXIT_SUCCESS; +} diff --git a/system/service/modp_b64/LICENSE b/system/service/modp_b64/LICENSE new file mode 100644 index 0000000000..55af76f3eb --- /dev/null +++ b/system/service/modp_b64/LICENSE @@ -0,0 +1,33 @@ + * MODP_B64 - High performance base64 encoder/decoder + * Version 1.3 -- 17-Mar-2006 + * http://modp.com/release/base64 + * + * Copyright (c) 2005, 2006 Nick Galbreath -- nickg [at] modp [dot] com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the modp.com nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/system/service/modp_b64/README.chromium b/system/service/modp_b64/README.chromium new file mode 100644 index 0000000000..35b9e54840 --- /dev/null +++ b/system/service/modp_b64/README.chromium @@ -0,0 +1,15 @@ +Name: modp base64 decoder +Short Name: stringencoders +URL: http://code.google.com/p/stringencoders/ +Version: unknown +License: BSD +Security Critical: yes + +Description: +The modp_b64.c file was modified to remove the inclusion of modp's config.h +and to fix compilation errors that occur under VC8. The file was renamed +modp_b64.cc to force it to be compiled as C++ so that the inclusion of +basictypes.h could be possible. + +The modp_b64.cc and modp_b64.h files were modified to make them safe on +64-bit systems. diff --git a/system/service/modp_b64/modp_b64.cpp b/system/service/modp_b64/modp_b64.cpp new file mode 100644 index 0000000000..e5f6cf1024 --- /dev/null +++ b/system/service/modp_b64/modp_b64.cpp @@ -0,0 +1,265 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ +/** + * \file + * <PRE> + * MODP_B64 - High performance base64 encoder/decoder + * Version 1.3 -- 17-Mar-2006 + * http://modp.com/release/base64 + * + * Copyright © 2005, 2006 Nick Galbreath -- nickg [at] modp [dot] com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the modp.com nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This is the standard "new" BSD license: + * http://www.opensource.org/licenses/bsd-license.php + * </PRE> + */ + +/* public header */ +#include "modp_b64.h" + +/* + * If you are ripping this out of the library, comment out the next + * line and uncomment the next lines as approrpiate + */ +//#include "config.h" + +/* if on motoral, sun, ibm; uncomment this */ +/* #define WORDS_BIGENDIAN 1 */ +/* else for Intel, Amd; uncomment this */ +/* #undef WORDS_BIGENDIAN */ + +#include "modp_b64_data.h" + +#define BADCHAR 0x01FFFFFF + +/** + * you can control if we use padding by commenting out this + * next line. However, I highly recommend you use padding and not + * using it should only be for compatability with a 3rd party. + * Also, 'no padding' is not tested! + */ +#define DOPAD 1 + +/* + * if we aren't doing padding + * set the pad character to NULL + */ +#ifndef DOPAD +#undef CHARPAD +#define CHARPAD '\0' +#endif + +size_t modp_b64_encode(char* dest, const char* str, size_t len) +{ + size_t i = 0; + uint8_t* p = (uint8_t*) dest; + + /* unsigned here is important! */ + uint8_t t1, t2, t3; + + if (len > 2) { + for (; i < len - 2; i += 3) { + t1 = str[i]; t2 = str[i+1]; t3 = str[i+2]; + *p++ = e0[t1]; + *p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *p++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *p++ = e2[t3]; + } + } + + switch (len - i) { + case 0: + break; + case 1: + t1 = str[i]; + *p++ = e0[t1]; + *p++ = e1[(t1 & 0x03) << 4]; + *p++ = CHARPAD; + *p++ = CHARPAD; + break; + default: /* case 2 */ + t1 = str[i]; t2 = str[i+1]; + *p++ = e0[t1]; + *p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *p++ = e2[(t2 & 0x0F) << 2]; + *p++ = CHARPAD; + } + + *p = '\0'; + return p - (uint8_t*)dest; +} + +#ifdef WORDS_BIGENDIAN /* BIG ENDIAN -- SUN / IBM / MOTOROLA */ +int modp_b64_decode(char* dest, const char* src, int len) +{ + if (len == 0) return 0; + +#ifdef DOPAD + /* if padding is used, then the message must be at least + 4 chars and be a multiple of 4. + there can be at most 2 pad chars at the end */ + if (len < 4 || (len % 4 != 0)) return MODP_B64_ERROR; + if (src[len-1] == CHARPAD) { + len--; + if (src[len -1] == CHARPAD) { + len--; + } + } +#endif /* DOPAD */ + + size_t i; + int leftover = len % 4; + size_t chunks = (leftover == 0) ? len / 4 - 1 : len /4; + + uint8_t* p = (uint8_t*) dest; + uint32_t x = 0; + uint32_t* destInt = (uint32_t*) p; + uint32_t* srcInt = (uint32_t*) src; + uint32_t y = *srcInt++; + for (i = 0; i < chunks; ++i) { + x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] | + d2[y >> 8 & 0xff] | d3[y & 0xff]; + + if (x >= BADCHAR) return MODP_B64_ERROR; + *destInt = x << 8; + p += 3; + destInt = (uint32_t*)p; + y = *srcInt++; + } + + switch (leftover) { + case 0: + x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] | + d2[y >> 8 & 0xff] | d3[y & 0xff]; + if (x >= BADCHAR) return MODP_B64_ERROR; + *p++ = ((uint8_t*)&x)[1]; + *p++ = ((uint8_t*)&x)[2]; + *p = ((uint8_t*)&x)[3]; + return (chunks+1)*3; + case 1: + x = d3[y >> 24]; + *p = (uint8_t)x; + break; + case 2: + x = d3[y >> 24] *64 + d3[(y >> 16) & 0xff]; + *p = (uint8_t)(x >> 4); + break; + default: /* case 3 */ + x = (d3[y >> 24] *64 + d3[(y >> 16) & 0xff])*64 + + d3[(y >> 8) & 0xff]; + *p++ = (uint8_t) (x >> 10); + *p = (uint8_t) (x >> 2); + break; + } + + if (x >= BADCHAR) return MODP_B64_ERROR; + return 3*chunks + (6*leftover)/8; +} + +#else /* LITTLE ENDIAN -- INTEL AND FRIENDS */ + +size_t modp_b64_decode(char* dest, const char* src, size_t len) +{ + if (len == 0) return 0; + +#ifdef DOPAD + /* + * if padding is used, then the message must be at least + * 4 chars and be a multiple of 4 + */ + if (len < 4 || (len % 4 != 0)) return MODP_B64_ERROR; /* error */ + /* there can be at most 2 pad chars at the end */ + if (src[len-1] == CHARPAD) { + len--; + if (src[len -1] == CHARPAD) { + len--; + } + } +#endif + + size_t i; + int leftover = len % 4; + size_t chunks = (leftover == 0) ? len / 4 - 1 : len /4; + + uint8_t* p = (uint8_t*)dest; + uint32_t x = 0; + uint32_t* destInt = (uint32_t*) p; + uint32_t* srcInt = (uint32_t*) src; + uint32_t y = *srcInt++; + for (i = 0; i < chunks; ++i) { + x = d0[y & 0xff] | + d1[(y >> 8) & 0xff] | + d2[(y >> 16) & 0xff] | + d3[(y >> 24) & 0xff]; + + if (x >= BADCHAR) return MODP_B64_ERROR; + *destInt = x ; + p += 3; + destInt = (uint32_t*)p; + y = *srcInt++;} + + + switch (leftover) { + case 0: + x = d0[y & 0xff] | + d1[(y >> 8) & 0xff] | + d2[(y >> 16) & 0xff] | + d3[(y >> 24) & 0xff]; + + if (x >= BADCHAR) return MODP_B64_ERROR; + *p++ = ((uint8_t*)(&x))[0]; + *p++ = ((uint8_t*)(&x))[1]; + *p = ((uint8_t*)(&x))[2]; + return (chunks+1)*3; + break; + case 1: /* with padding this is an impossible case */ + x = d0[y & 0xff]; + *p = *((uint8_t*)(&x)); // i.e. first char/byte in int + break; + case 2: // * case 2, 1 output byte */ + x = d0[y & 0xff] | d1[y >> 8 & 0xff]; + *p = *((uint8_t*)(&x)); // i.e. first char + break; + default: /* case 3, 2 output bytes */ + x = d0[y & 0xff] | + d1[y >> 8 & 0xff ] | + d2[y >> 16 & 0xff]; /* 0x3c */ + *p++ = ((uint8_t*)(&x))[0]; + *p = ((uint8_t*)(&x))[1]; + break; + } + + if (x >= BADCHAR) return MODP_B64_ERROR; + + return 3*chunks + (6*leftover)/8; +} + +#endif /* if bigendian / else / endif */ diff --git a/system/service/modp_b64/modp_b64.h b/system/service/modp_b64/modp_b64.h new file mode 100644 index 0000000000..3270e5fdf1 --- /dev/null +++ b/system/service/modp_b64/modp_b64.h @@ -0,0 +1,171 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ + +/** + * \file + * <PRE> + * High performance base64 encoder / decoder + * Version 1.3 -- 17-Mar-2006 + * + * Copyright © 2005, 2006, Nick Galbreath -- nickg [at] modp [dot] com + * All rights reserved. + * + * http://modp.com/release/base64 + * + * Released under bsd license. See modp_b64.c for details. + * </pre> + * + * The default implementation is the standard b64 encoding with padding. + * It's easy to change this to use "URL safe" characters and to remove + * padding. See the modp_b64.c source code for details. + * + */ + +#ifndef MODP_B64 +#define MODP_B64 + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Encode a raw binary string into base 64. + * src contains the bytes + * len contains the number of bytes in the src + * dest should be allocated by the caller to contain + * at least modp_b64_encode_len(len) bytes (see below) + * This will contain the null-terminated b64 encoded result + * returns length of the destination string plus the ending null byte + * i.e. the result will be equal to strlen(dest) + 1 + * + * Example + * + * \code + * char* src = ...; + * int srclen = ...; //the length of number of bytes in src + * char* dest = (char*) malloc(modp_b64_encode_len); + * int len = modp_b64_encode(dest, src, sourcelen); + * if (len == -1) { + * printf("Error\n"); + * } else { + * printf("b64 = %s\n", dest); + * } + * \endcode + * + */ +size_t modp_b64_encode(char* dest, const char* str, size_t len); + +/** + * Decode a base64 encoded string + * + * src should contain exactly len bytes of b64 characters. + * if src contains -any- non-base characters (such as white + * space, -1 is returned. + * + * dest should be allocated by the caller to contain at least + * len * 3 / 4 bytes. + * + * Returns the length (strlen) of the output, or -1 if unable to + * decode + * + * \code + * char* src = ...; + * int srclen = ...; // or if you don't know use strlen(src) + * char* dest = (char*) malloc(modp_b64_decode_len(srclen)); + * int len = modp_b64_decode(dest, src, sourcelen); + * if (len == -1) { error } + * \endcode + */ +size_t modp_b64_decode(char* dest, const char* src, size_t len); + +/** + * Given a source string of length len, this returns the amount of + * memory the destination string should have. + * + * remember, this is integer math + * 3 bytes turn into 4 chars + * ceiling[len / 3] * 4 + 1 + * + * +1 is for any extra null. + */ +#define modp_b64_encode_len(A) ((A+2)/3 * 4 + 1) + +/** + * Given a base64 string of length len, + * this returns the amount of memory required for output string + * It maybe be more than the actual number of bytes written. + * NOTE: remember this is integer math + * this allocates a bit more memory than traditional versions of b64 + * decode 4 chars turn into 3 bytes + * floor[len * 3/4] + 2 + */ +#define modp_b64_decode_len(A) (A / 4 * 3 + 2) + +/** + * Will return the strlen of the output from encoding. + * This may be less than the required number of bytes allocated. + * + * This allows you to 'deserialized' a struct + * \code + * char* b64encoded = "..."; + * int len = strlen(b64encoded); + * + * struct datastuff foo; + * if (modp_b64_encode_strlen(sizeof(struct datastuff)) != len) { + * // wrong size + * return false; + * } else { + * // safe to do; + * if (modp_b64_decode((char*) &foo, b64encoded, len) == -1) { + * // bad characters + * return false; + * } + * } + * // foo is filled out now + * \endcode + */ +#define modp_b64_encode_strlen(A) ((A + 2)/ 3 * 4) + +#define MODP_B64_ERROR ((size_t)-1) + +#ifdef __cplusplus +} + +#include <string> + +inline std::string& modp_b64_encode(std::string& s) +{ + std::string x(modp_b64_encode_len(s.size()), '\0'); + size_t d = modp_b64_encode(const_cast<char*>(x.data()), s.data(), (int)s.size()); + x.erase(d, std::string::npos); + s.swap(x); + return s; +} + +/** + * base 64 decode a string (self-modifing) + * On failure, the string is empty. + * + * This function is for C++ only (duh) + * + * \param[in,out] s the string to be decoded + * \return a reference to the input string + */ +inline std::string& modp_b64_decode(std::string& s) +{ + std::string x(modp_b64_decode_len(s.size()), '\0'); + size_t d = modp_b64_decode(const_cast<char*>(x.data()), s.data(), (int)s.size()); + if (d == MODP_B64_ERROR) { + x.clear(); + } else { + x.erase(d, std::string::npos); + } + s.swap(x); + return s; +} + +#endif /* __cplusplus */ + +#endif /* MODP_B64 */ diff --git a/system/service/modp_b64/modp_b64_data.h b/system/service/modp_b64/modp_b64_data.h new file mode 100644 index 0000000000..9a777b8452 --- /dev/null +++ b/system/service/modp_b64/modp_b64_data.h @@ -0,0 +1,482 @@ +//#include "build/build_config.h" +#include <stdint.h> + +#define CHAR62 '+' +#define CHAR63 '/' +#define CHARPAD '=' +static const char e0[256] = { + 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', + 'C', 'C', 'D', 'D', 'D', 'D', 'E', 'E', 'E', 'E', + 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', + 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', + 'K', 'K', 'K', 'K', 'L', 'L', 'L', 'L', 'M', 'M', + 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', + 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', + 'R', 'R', 'S', 'S', 'S', 'S', 'T', 'T', 'T', 'T', + 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', + 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', + 'Z', 'Z', 'Z', 'Z', 'a', 'a', 'a', 'a', 'b', 'b', + 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', + 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', + 'g', 'g', 'h', 'h', 'h', 'h', 'i', 'i', 'i', 'i', + 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', + 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', + 'o', 'o', 'o', 'o', 'p', 'p', 'p', 'p', 'q', 'q', + 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', + 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', + 'v', 'v', 'w', 'w', 'w', 'w', 'x', 'x', 'x', 'x', + 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', + '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', + '3', '3', '3', '3', '4', '4', '4', '4', '5', '5', + '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', + '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', + '+', '+', '/', '/', '/', '/' +}; + +static const char e1[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '+', '/' +}; + +static const char e2[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '+', '/' +}; + + + +#ifdef WORDS_BIGENDIAN + + +/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */ + +static const uint32_t d0[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00f80000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00fc0000, +0x00d00000, 0x00d40000, 0x00d80000, 0x00dc0000, 0x00e00000, 0x00e40000, +0x00e80000, 0x00ec0000, 0x00f00000, 0x00f40000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00040000, 0x00080000, 0x000c0000, 0x00100000, 0x00140000, 0x00180000, +0x001c0000, 0x00200000, 0x00240000, 0x00280000, 0x002c0000, 0x00300000, +0x00340000, 0x00380000, 0x003c0000, 0x00400000, 0x00440000, 0x00480000, +0x004c0000, 0x00500000, 0x00540000, 0x00580000, 0x005c0000, 0x00600000, +0x00640000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00680000, 0x006c0000, 0x00700000, 0x00740000, 0x00780000, +0x007c0000, 0x00800000, 0x00840000, 0x00880000, 0x008c0000, 0x00900000, +0x00940000, 0x00980000, 0x009c0000, 0x00a00000, 0x00a40000, 0x00a80000, +0x00ac0000, 0x00b00000, 0x00b40000, 0x00b80000, 0x00bc0000, 0x00c00000, +0x00c40000, 0x00c80000, 0x00cc0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d1[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0003e000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0003f000, +0x00034000, 0x00035000, 0x00036000, 0x00037000, 0x00038000, 0x00039000, +0x0003a000, 0x0003b000, 0x0003c000, 0x0003d000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00010000, 0x00011000, 0x00012000, +0x00013000, 0x00014000, 0x00015000, 0x00016000, 0x00017000, 0x00018000, +0x00019000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0001a000, 0x0001b000, 0x0001c000, 0x0001d000, 0x0001e000, +0x0001f000, 0x00020000, 0x00021000, 0x00022000, 0x00023000, 0x00024000, +0x00025000, 0x00026000, 0x00027000, 0x00028000, 0x00029000, 0x0002a000, +0x0002b000, 0x0002c000, 0x0002d000, 0x0002e000, 0x0002f000, 0x00030000, +0x00031000, 0x00032000, 0x00033000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d2[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000f80, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000fc0, +0x00000d00, 0x00000d40, 0x00000d80, 0x00000dc0, 0x00000e00, 0x00000e40, +0x00000e80, 0x00000ec0, 0x00000f00, 0x00000f40, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000040, 0x00000080, 0x000000c0, 0x00000100, 0x00000140, 0x00000180, +0x000001c0, 0x00000200, 0x00000240, 0x00000280, 0x000002c0, 0x00000300, +0x00000340, 0x00000380, 0x000003c0, 0x00000400, 0x00000440, 0x00000480, +0x000004c0, 0x00000500, 0x00000540, 0x00000580, 0x000005c0, 0x00000600, +0x00000640, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000680, 0x000006c0, 0x00000700, 0x00000740, 0x00000780, +0x000007c0, 0x00000800, 0x00000840, 0x00000880, 0x000008c0, 0x00000900, +0x00000940, 0x00000980, 0x000009c0, 0x00000a00, 0x00000a40, 0x00000a80, +0x00000ac0, 0x00000b00, 0x00000b40, 0x00000b80, 0x00000bc0, 0x00000c00, +0x00000c40, 0x00000c80, 0x00000cc0, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d3[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000003e, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000003f, +0x00000034, 0x00000035, 0x00000036, 0x00000037, 0x00000038, 0x00000039, +0x0000003a, 0x0000003b, 0x0000003c, 0x0000003d, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, +0x00000007, 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, +0x0000000d, 0x0000000e, 0x0000000f, 0x00000010, 0x00000011, 0x00000012, +0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, 0x00000018, +0x00000019, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000001a, 0x0000001b, 0x0000001c, 0x0000001d, 0x0000001e, +0x0000001f, 0x00000020, 0x00000021, 0x00000022, 0x00000023, 0x00000024, +0x00000025, 0x00000026, 0x00000027, 0x00000028, 0x00000029, 0x0000002a, +0x0000002b, 0x0000002c, 0x0000002d, 0x0000002e, 0x0000002f, 0x00000030, +0x00000031, 0x00000032, 0x00000033, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +#else + + +/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */ + +static const uint32_t d0[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, +0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, +0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, +0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, +0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, +0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, +0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, +0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, +0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, +0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, +0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d1[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, +0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, +0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, +0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, +0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, +0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, +0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, +0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, +0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d2[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, +0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, +0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, +0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, +0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, +0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, +0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, +0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, +0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, +0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, +0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d3[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, +0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, +0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, +0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, +0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, +0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, +0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, +0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, +0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, +0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, +0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +#endif diff --git a/system/service/uuid.cpp b/system/service/uuid.cpp new file mode 100644 index 0000000000..13e21a1488 --- /dev/null +++ b/system/service/uuid.cpp @@ -0,0 +1,86 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "uuid.h" + +#include <algorithm> +#include <array> +#include <stack> +#include <string> + +namespace bluetooth { + +void Uuid::InitializeDefault() { + // Initialize to base bluetooth UUID. + id_ = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}; +} + +Uuid::Uuid() { + InitializeDefault(); +} + +Uuid::Uuid(const std::string& uuid) { + InitializeDefault(); + const int start_index = uuid.size() == 4 ? 2 : 0; + const size_t copy_size = std::min(id_.size(), uuid.size() / 2); + for (size_t i = 0; i < copy_size; ++i) { + std::string octet_text(uuid, i * 2, 2); + id_[start_index + i] = std::stoul(octet_text, 0, 16); + } +} + +Uuid::Uuid(const bt_uuid_t& uuid) { + std::reverse_copy(uuid.uu, uuid.uu + sizeof(uuid.uu), id_.begin()); +} + +Uuid::Uuid(const Uuid::Uuid16Bit& uuid) { + InitializeDefault(); + std::copy(uuid.begin(), uuid.end(), id_.begin() + kUuid16Octets); +} + +Uuid::Uuid(const Uuid::Uuid32Bit& uuid) { + InitializeDefault(); + std::copy(uuid.begin(), uuid.end(), id_.begin()); +} + +Uuid::Uuid(const Uuid::Uuid128Bit& uuid) : id_(uuid) {} + +const Uuid::Uuid128Bit Uuid::GetFullBigEndian() const { + return id_; +} + +const Uuid::Uuid128Bit Uuid::GetFullLittleEndian() const { + Uuid::Uuid128Bit ret; + std::reverse_copy(id_.begin(), id_.end(), ret.begin()); + return ret; +} + +const bt_uuid_t Uuid::GetBlueDroid() const { + bt_uuid_t ret; + std::reverse_copy(id_.begin(), id_.end(), ret.uu); + return ret; +} + +bool Uuid::operator<(const Uuid& rhs) const { + return std::lexicographical_compare(id_.begin(), id_.end(), rhs.id_.begin(), + rhs.id_.end()); +} + +bool Uuid::operator==(const Uuid& rhs) const { + return std::equal(id_.begin(), id_.end(), rhs.id_.begin()); +} + +} // namespace bluetooth diff --git a/system/service/uuid.h b/system/service/uuid.h new file mode 100644 index 0000000000..0226ef623d --- /dev/null +++ b/system/service/uuid.h @@ -0,0 +1,69 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include <array> +#include <string> + +#include "hardware/bluetooth.h" + +namespace bluetooth { + +class Uuid { + public: + enum Type { + kUuid128Octets = 16, + kUuid32Octets = 4, + kUuid16Octets = 2, + }; + + typedef std::array<uint8_t, Uuid::kUuid16Octets> Uuid16Bit; + typedef std::array<uint8_t, Uuid::kUuid32Octets> Uuid32Bit; + typedef std::array<uint8_t, Uuid::kUuid128Octets> Uuid128Bit; + + // Construct a Bluetooth 'base' UUID. + Uuid(); + + // BlueDroid constructor. + explicit Uuid(const bt_uuid_t& uuid); + + // String constructor. Only hex ASCII accepted. + explicit Uuid(const std::string& uuid); + + // std::array variants constructors. + explicit Uuid(const Uuid::Uuid16Bit& uuid); + explicit Uuid(const Uuid::Uuid32Bit& uuid); + explicit Uuid(const Uuid::Uuid128Bit& uuid); + + // Provide the full network-byte-ordered blob. + const Uuid128Bit GetFullBigEndian() const; + + // Provide blob in Little endian (BlueDroid expects this). + const Uuid128Bit GetFullLittleEndian() const; + + // Helper for bluedroid LE type. + const bt_uuid_t GetBlueDroid() const; + + bool operator<(const Uuid& rhs) const; + bool operator==(const Uuid& rhs) const; + + private: + void InitializeDefault(); + // Network-byte-ordered ID. + Uuid128Bit id_; +}; + +} // namespace bluetooth diff --git a/system/service/uuid_test.cpp b/system/service/uuid_test.cpp new file mode 100644 index 0000000000..8009967234 --- /dev/null +++ b/system/service/uuid_test.cpp @@ -0,0 +1,125 @@ +// +// Copyright (C) 2015 Google, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include <algorithm> +#include <array> +#include <stdint.h> + +#include <gtest/gtest.h> + +#include "uuid.h" + +using namespace bluetooth; + +struct UuidTest : public ::testing::Test { + typedef std::array<uint8_t, Uuid::kUuid128Octets> UuidData; + + UuidTest() + : kBtSigBaseUuid({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}) + {} + + const UuidData kBtSigBaseUuid; +}; + +// Verify that an uninitialized Uuid is equal +// To the BT SIG Base UUID. +TEST_F(UuidTest, DefaultUuid) { + Uuid uuid; + ASSERT_TRUE(uuid.GetFullBigEndian() == kBtSigBaseUuid); +} + +// Verify that we initialize a 16-bit UUID in a +// way consistent with how we read it. +TEST_F(UuidTest, Init16Bit) { + auto My16BitUuid = kBtSigBaseUuid; + My16BitUuid[2] = 0xde; + My16BitUuid[3] = 0xad; + Uuid uuid(Uuid::Uuid16Bit({0xde, 0xad})); + ASSERT_TRUE(uuid.GetFullBigEndian() == My16BitUuid); +} + +// Verify that we initialize a 16-bit UUID in a +// way consistent with how we read it. +TEST_F(UuidTest, Init16BitString) { + auto My16BitUuid = kBtSigBaseUuid; + My16BitUuid[2] = 0xde; + My16BitUuid[3] = 0xad; + Uuid uuid("dead"); + ASSERT_TRUE(uuid.GetFullBigEndian() == My16BitUuid); +} + + +// Verify that we initialize a 32-bit UUID in a +// way consistent with how we read it. +TEST_F(UuidTest, Init32Bit) { + auto My32BitUuid = kBtSigBaseUuid; + My32BitUuid[0] = 0xde; + My32BitUuid[1] = 0xad; + My32BitUuid[2] = 0xbe; + My32BitUuid[3] = 0xef; + Uuid uuid(Uuid::Uuid32Bit({0xde, 0xad, 0xbe, 0xef})); + ASSERT_TRUE(uuid.GetFullBigEndian() == My32BitUuid); +} + +// Verify correct reading of a 32-bit UUID initialized from string. +TEST_F(UuidTest, Init32BitString) { + auto My32BitUuid = kBtSigBaseUuid; + My32BitUuid[0] = 0xde; + My32BitUuid[1] = 0xad; + My32BitUuid[2] = 0xbe; + My32BitUuid[3] = 0xef; + Uuid uuid("deadbeef"); + ASSERT_TRUE(uuid.GetFullBigEndian() == My32BitUuid); +} + +// Verify that we initialize a 128-bit UUID in a +// way consistent with how we read it. +TEST_F(UuidTest, Init128Bit) { + auto My128BitUuid = kBtSigBaseUuid; + for (int i = 0; i < static_cast<int>(My128BitUuid.size()); ++i) { + My128BitUuid[i] = i; + } + + Uuid uuid(My128BitUuid); + ASSERT_TRUE(uuid.GetFullBigEndian() == My128BitUuid); +} + +// Verify that we initialize a 128-bit UUID in a +// way consistent with how we read it as LE. +TEST_F(UuidTest, Init128BitLittleEndian) { + auto My128BitUuid = kBtSigBaseUuid; + for (int i = 0; i < static_cast<int>(My128BitUuid.size()); ++i) { + My128BitUuid[i] = i; + } + + Uuid uuid(My128BitUuid); + std::reverse(My128BitUuid.begin(), My128BitUuid.end()); + ASSERT_TRUE(uuid.GetFullLittleEndian() == My128BitUuid); +} + +// Verify that we initialize a 128-bit UUID in a +// way consistent with how we read it. +TEST_F(UuidTest, Init128BitString) { + auto My128BitUuid = kBtSigBaseUuid; + for (int i = 0; i < static_cast<int>(My128BitUuid.size()); ++i) { + My128BitUuid[i] = i; + } + + std::string uuid_text("000102030405060708090A0B0C0D0E0F"); + ASSERT_TRUE(uuid_text.size() == (16 * 2)); + Uuid uuid(uuid_text); + ASSERT_TRUE(uuid.GetFullBigEndian() == My128BitUuid); +} |