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 &copy; 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 &copy; 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);
+}