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
diff --git a/service/Android.mk b/service/Android.mk
new file mode 100644
index 0000000..f450840
--- /dev/null
+++ b/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/service/a2dp_source.cpp b/service/a2dp_source.cpp
new file mode 100644
index 0000000..6cde92a
--- /dev/null
+++ b/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/service/a2dp_source.h b/service/a2dp_source.h
new file mode 100644
index 0000000..557b98f
--- /dev/null
+++ b/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/service/base/base64.cpp b/service/base/base64.cpp
new file mode 100644
index 0000000..704d678
--- /dev/null
+++ b/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/service/base/base64.h b/service/base/base64.h
new file mode 100644
index 0000000..38923af
--- /dev/null
+++ b/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/service/base/string_number_conversions.cpp b/service/base/string_number_conversions.cpp
new file mode 100644
index 0000000..62c3d48
--- /dev/null
+++ b/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/service/base/string_number_conversions.h b/service/base/string_number_conversions.h
new file mode 100644
index 0000000..7d1d737
--- /dev/null
+++ b/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/service/base/string_split.cpp b/service/base/string_split.cpp
new file mode 100644
index 0000000..670f2e4
--- /dev/null
+++ b/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/service/base/string_split.h b/service/base/string_split.h
new file mode 100644
index 0000000..ee1a13f
--- /dev/null
+++ b/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/service/core_stack.cpp b/service/core_stack.cpp
new file mode 100644
index 0000000..d40d64b
--- /dev/null
+++ b/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/service/core_stack.h b/service/core_stack.h
new file mode 100644
index 0000000..8b5671a
--- /dev/null
+++ b/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/service/gatt_server.cpp b/service/gatt_server.cpp
new file mode 100644
index 0000000..54e407e
--- /dev/null
+++ b/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/service/gatt_server.h b/service/gatt_server.h
new file mode 100644
index 0000000..2328395
--- /dev/null
+++ b/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/service/host.cpp b/service/host.cpp
new file mode 100644
index 0000000..fb38f18
--- /dev/null
+++ b/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/service/host.h b/service/host.h
new file mode 100644
index 0000000..e695717
--- /dev/null
+++ b/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/service/logging_helpers.cpp b/service/logging_helpers.cpp
new file mode 100644
index 0000000..59cde12
--- /dev/null
+++ b/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/service/logging_helpers.h b/service/logging_helpers.h
new file mode 100644
index 0000000..6baafa8
--- /dev/null
+++ b/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/service/main.cpp b/service/main.cpp
new file mode 100644
index 0000000..a818f74
--- /dev/null
+++ b/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/service/modp_b64/LICENSE b/service/modp_b64/LICENSE
new file mode 100644
index 0000000..55af76f
--- /dev/null
+++ b/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/service/modp_b64/README.chromium b/service/modp_b64/README.chromium
new file mode 100644
index 0000000..35b9e54
--- /dev/null
+++ b/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/service/modp_b64/modp_b64.cpp b/service/modp_b64/modp_b64.cpp
new file mode 100644
index 0000000..e5f6cf1
--- /dev/null
+++ b/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/service/modp_b64/modp_b64.h b/service/modp_b64/modp_b64.h
new file mode 100644
index 0000000..3270e5f
--- /dev/null
+++ b/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/service/modp_b64/modp_b64_data.h b/service/modp_b64/modp_b64_data.h
new file mode 100644
index 0000000..9a777b8
--- /dev/null
+++ b/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/service/uuid.cpp b/service/uuid.cpp
new file mode 100644
index 0000000..13e21a1
--- /dev/null
+++ b/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/service/uuid.h b/service/uuid.h
new file mode 100644
index 0000000..0226ef6
--- /dev/null
+++ b/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/service/uuid_test.cpp b/service/uuid_test.cpp
new file mode 100644
index 0000000..8009967
--- /dev/null
+++ b/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);
+}