Bug 1114515 - Par h4: Implement Service, Characteristic, Descriptor interfaces for GATT client DiscoverServices API. r=btian, r=mrbkap
authorJocelyn Liu <joliu@mozilla.com>
Mon, 23 Mar 2015 03:27:00 -0400
changeset 265422 c16ac2a8f47c3f245ad67a88088b0872767cd497
parent 265421 9fce71f4c4aa4b9dbe15b078a067fed6c7b9fc49
child 265423 5aaf90d7a1e3e9f2dfe27d4f82e5938bb10bbd70
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbtian, mrbkap
bugs1114515
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1114515 - Par h4: Implement Service, Characteristic, Descriptor interfaces for GATT client DiscoverServices API. r=btian, r=mrbkap This patch covers 1) Add BluetoothGattService, BluetoothGattCharacteristic, BluetoothGattDescriptor interfaces 2) Create services, characteristics, descriptors from handling signals distributed by BluetoothGattManager
dom/bindings/Bindings.conf
dom/bluetooth2/BluetoothGatt.cpp
dom/bluetooth2/BluetoothGatt.h
dom/bluetooth2/BluetoothGattCharacteristic.cpp
dom/bluetooth2/BluetoothGattCharacteristic.h
dom/bluetooth2/BluetoothGattDescriptor.cpp
dom/bluetooth2/BluetoothGattDescriptor.h
dom/bluetooth2/BluetoothGattService.cpp
dom/bluetooth2/BluetoothGattService.h
dom/bluetooth2/moz.build
dom/webidl/BluetoothGatt.webidl
dom/webidl/BluetoothGattCharacteristic.webidl
dom/webidl/BluetoothGattDescriptor.webidl
dom/webidl/BluetoothGattService.webidl
dom/webidl/moz.build
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -154,16 +154,28 @@ DOMInterfaces = {
 'BluetoothDiscoveryHandle': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothDiscoveryHandle',
 },
 
 'BluetoothGatt': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothGatt',
 },
 
+'BluetoothGattCharacteristic': {
+    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattCharacteristic',
+},
+
+'BluetoothGattDescriptor': {
+    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattDescriptor',
+},
+
+'BluetoothGattService': {
+    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattService',
+},
+
 'BluetoothManager': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
 },
 
 'BluetoothPairingHandle': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothPairingHandle',
 },
 
--- a/dom/bluetooth2/BluetoothGatt.cpp
+++ b/dom/bluetooth2/BluetoothGatt.cpp
@@ -1,38 +1,33 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothUtils.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
 #include "mozilla/dom/bluetooth/BluetoothGatt.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/BluetoothGattBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "nsIUUIDGenerator.h"
 #include "nsServiceManagerUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothGatt)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothGatt,
-                                                DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothGatt,
-                                                  DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothGatt,
+                                   DOMEventTargetHelper,
+                                   mServices)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothGatt)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothGatt, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothGatt, DOMEventTargetHelper)
 
 BluetoothGatt::BluetoothGatt(nsPIDOMWindow* aWindow,
@@ -262,16 +257,32 @@ BluetoothGatt::UpdateConnectionState(Blu
                         false,
                         false);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   DispatchTrustedEvent(event);
 }
 
 void
+BluetoothGatt::HandleServicesDiscovered(const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattServiceId);
+
+  const InfallibleTArray<BluetoothGattServiceId>& serviceIds =
+    aValue.get_ArrayOfBluetoothGattServiceId();
+
+  for (uint32_t i = 0; i < serviceIds.Length(); i++) {
+    mServices.AppendElement(new BluetoothGattService(
+      GetParentObject(), mAppUuid, serviceIds[i]));
+  }
+
+  BluetoothGattBinding::ClearCachedServicesValue(this);
+}
+
+void
 BluetoothGatt::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("ClientRegistered")) {
     MOZ_ASSERT(v.type() == BluetoothValue::Tuint32_t);
     mClientIf = v.get_uint32_t();
@@ -279,16 +290,28 @@ BluetoothGatt::Notify(const BluetoothSig
     mClientIf = 0;
   } else if (aData.name().EqualsLiteral(GATT_CONNECTION_STATE_CHANGED_ID)) {
     MOZ_ASSERT(v.type() == BluetoothValue::Tbool);
 
     BluetoothConnectionState state =
       v.get_bool() ? BluetoothConnectionState::Connected
                    : BluetoothConnectionState::Disconnected;
     UpdateConnectionState(state);
+  } else if (aData.name().EqualsLiteral("ServicesDiscovered")) {
+    HandleServicesDiscovered(v);
+  } else if (aData.name().EqualsLiteral("DiscoverCompleted")) {
+    MOZ_ASSERT(v.type() == BluetoothValue::Tbool);
+
+    bool isDiscoverSuccess = v.get_bool();
+    if (!isDiscoverSuccess) { // Clean all discovered attributes if failed
+      mServices.Clear();
+      BluetoothGattBinding::ClearCachedServicesValue(this);
+    }
+
+    mDiscoveringServices = false;
   } else {
     BT_WARNING("Not handling GATT signal: %s",
                NS_ConvertUTF16toUTF8(aData.name()).get());
   }
 }
 
 JSObject*
 BluetoothGatt::WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto)
--- a/dom/bluetooth2/BluetoothGatt.h
+++ b/dom/bluetooth2/BluetoothGatt.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothgatt_h__
 #define mozilla_dom_bluetooth_bluetoothgatt_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/BluetoothGattBinding.h"
 #include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattService.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 class Promise;
 }
 }
 
@@ -36,16 +37,21 @@ public:
   /****************************************************************************
    * Attribute Getters
    ***************************************************************************/
   BluetoothConnectionState ConnectionState() const
   {
     return mConnectionState;
   }
 
+  void GetServices(nsTArray<nsRefPtr<BluetoothGattService>>& aServices) const
+  {
+    aServices = mServices;
+  }
+
   /****************************************************************************
    * Event Handlers
    ***************************************************************************/
   IMPL_EVENT_HANDLER(connectionstatechanged);
 
   /****************************************************************************
    * Methods (Web API Implementation)
    ***************************************************************************/
@@ -83,16 +89,25 @@ private:
 
   /**
    * Generate a random uuid.
    *
    * @param aUuidString [out] String to store the generated uuid.
    */
   void GenerateUuid(nsAString &aUuidString);
 
+  /**
+   * Add newly discovered GATT services into mServices and update the cache
+   * value of mServices.
+   *
+   * @param aValue [in] BluetoothValue which contains an array of
+   *                    BluetoothGattServiceId of all discovered services.
+   */
+  void HandleServicesDiscovered(const BluetoothValue& aValue);
+
   /****************************************************************************
    * Variables
    ***************************************************************************/
   /**
    * Random generated UUID of this GATT client.
    */
   nsString mAppUuid;
 
@@ -108,16 +123,21 @@ private:
   BluetoothConnectionState mConnectionState;
 
   /**
    * Address of the remote device.
    */
   nsString mDeviceAddr;
 
   /**
+   * Array of discovered services from the remote GATT server.
+   */
+  nsTArray<nsRefPtr<BluetoothGattService>> mServices;
+
+  /**
    * Indicate whether there is ongoing discoverServices request or not.
    */
   bool mDiscoveringServices;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattCharacteristic.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
+#include "mozilla/dom/BluetoothGattCharacteristicBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
+#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
+#include "mozilla/dom/bluetooth/BluetoothGattService.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+USING_BLUETOOTH_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
+  BluetoothGattCharacteristic, mOwner, mService, mDescriptors)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattCharacteristic)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattCharacteristic)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattCharacteristic)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+BluetoothGattCharacteristic::BluetoothGattCharacteristic(
+  nsPIDOMWindow* aOwner,
+  BluetoothGattService* aService,
+  const BluetoothGattId& aCharId)
+  : mOwner(aOwner)
+  , mService(aService)
+  , mCharId(aCharId)
+{
+  MOZ_ASSERT(aOwner);
+  MOZ_ASSERT(mService);
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  // Generate bluetooth signal path and a string representation to provide uuid
+  // of this characteristic to applications
+  nsString path;
+  GeneratePathFromGattId(mCharId, path, mUuidStr);
+  bs->RegisterBluetoothSignalHandler(path, this);
+}
+
+BluetoothGattCharacteristic::~BluetoothGattCharacteristic()
+{
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  nsString path;
+  GeneratePathFromGattId(mCharId, path);
+  bs->UnregisterBluetoothSignalHandler(path, this);
+}
+
+void
+BluetoothGattCharacteristic::HandleDescriptorsDiscovered(
+  const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattId);
+
+  const InfallibleTArray<BluetoothGattId>& descriptorIds =
+    aValue.get_ArrayOfBluetoothGattId();
+
+  for (uint32_t i = 0; i < descriptorIds.Length(); i++) {
+    mDescriptors.AppendElement(new BluetoothGattDescriptor(
+      GetParentObject(), this, descriptorIds[i]));
+  }
+
+  BluetoothGattCharacteristicBinding::ClearCachedDescriptorsValue(this);
+}
+
+void
+BluetoothGattCharacteristic::Notify(const BluetoothSignal& aData)
+{
+  BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+
+  BluetoothValue v = aData.value();
+  if (aData.name().EqualsLiteral("DescriptorsDiscovered")) {
+    HandleDescriptorsDiscovered(v);
+  } else {
+    BT_WARNING("Not handling GATT Characteristic signal: %s",
+               NS_ConvertUTF16toUTF8(aData.name()).get());
+  }
+}
+
+JSObject*
+BluetoothGattCharacteristic::WrapObject(JSContext* aContext,
+                                        JS::Handle<JSObject*> aGivenProto)
+{
+  return BluetoothGattCharacteristicBinding::Wrap(aContext, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattCharacteristic.h
@@ -0,0 +1,120 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothgattcharacteristic_h__
+#define mozilla_dom_bluetooth_bluetoothgattcharacteristic_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BluetoothGattCharacteristicBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+#include "nsPIDOMWindow.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothGattService;
+class BluetoothSignal;
+class BluetoothValue;
+
+class BluetoothGattCharacteristic final : public nsISupports
+                                        , public nsWrapperCache
+                                        , public BluetoothSignalObserver
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattCharacteristic)
+
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
+  BluetoothGattService* Service() const
+  {
+    return mService;
+  }
+
+  void GetDescriptors(
+    nsTArray<nsRefPtr<BluetoothGattDescriptor>>& aDescriptors) const
+  {
+    aDescriptors = mDescriptors;
+  }
+
+  void GetUuid(nsString& aUuidStr) const
+  {
+    aUuidStr = mUuidStr;
+  }
+
+  int InstanceId() const
+  {
+    return mCharId.mInstanceId;
+  }
+
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  const BluetoothGattId& GetCharacteristicId() const
+  {
+    return mCharId;
+  }
+
+  void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+     return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  BluetoothGattCharacteristic(nsPIDOMWindow* aOwner,
+                              BluetoothGattService* aService,
+                              const BluetoothGattId& aCharId);
+
+private:
+  ~BluetoothGattCharacteristic();
+
+  /**
+   * Add newly discovered GATT descriptors into mDescriptors and update the
+   * cache value of mDescriptors.
+   *
+   * @param aValue [in] BluetoothValue which contains an array of
+   *                    BluetoothGattId of all discovered descriptors.
+   */
+  void HandleDescriptorsDiscovered(const BluetoothValue& aValue);
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  nsCOMPtr<nsPIDOMWindow> mOwner;
+
+  /**
+   * Service that this characteristic belongs to.
+   */
+  nsRefPtr<BluetoothGattService> mService;
+
+  /**
+   * Array of discovered descriptors for this characteristic.
+   */
+  nsTArray<nsRefPtr<BluetoothGattDescriptor>> mDescriptors;
+
+  /**
+   * GattId of this GATT characteristic which contains
+   * 1) mUuid: UUID of this characteristic in byte array format.
+   * 2) mInstanceId: Instance id of this characteristic.
+   */
+  BluetoothGattId mCharId;
+
+  /**
+   * UUID string of this GATT characteristic.
+   */
+  nsString mUuidStr;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattDescriptor.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
+#include "mozilla/dom/BluetoothGattDescriptorBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
+#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+USING_BLUETOOTH_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
+  BluetoothGattDescriptor, mOwner, mCharacteristic)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattDescriptor)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattDescriptor)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattDescriptor)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+BluetoothGattDescriptor::BluetoothGattDescriptor(
+  nsPIDOMWindow* aOwner,
+  BluetoothGattCharacteristic* aCharacteristic,
+  const BluetoothGattId& aDescriptorId)
+  : mOwner(aOwner)
+  , mCharacteristic(aCharacteristic)
+  , mDescriptorId(aDescriptorId)
+{
+  MOZ_ASSERT(aOwner);
+  MOZ_ASSERT(aCharacteristic);
+
+  // Generate a string representation to provide uuid of this descriptor to
+  // applications
+  ReversedUuidToString(aDescriptorId.mUuid, mUuidStr);
+}
+
+BluetoothGattDescriptor::~BluetoothGattDescriptor()
+{
+}
+
+JSObject*
+BluetoothGattDescriptor::WrapObject(JSContext* aContext,
+                                    JS::Handle<JSObject*> aGivenProto)
+{
+  return BluetoothGattDescriptorBinding::Wrap(aContext, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattDescriptor.h
@@ -0,0 +1,88 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothgattdescriptor_h__
+#define mozilla_dom_bluetooth_bluetoothgattdescriptor_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BluetoothGattDescriptorBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+#include "nsPIDOMWindow.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothGattCharacteristic;
+class BluetoothSignal;
+class BluetoothValue;
+
+class BluetoothGattDescriptor final : public nsISupports
+                                    , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattDescriptor)
+
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
+  BluetoothGattCharacteristic* Characteristic() const
+  {
+    return mCharacteristic;
+  }
+
+  void GetUuid(nsString& aUuidStr) const
+  {
+    aUuidStr = mUuidStr;
+  }
+
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+     return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  BluetoothGattDescriptor(nsPIDOMWindow* aOwner,
+                          BluetoothGattCharacteristic* aCharacteristic,
+                          const BluetoothGattId& aDescriptorId);
+
+private:
+  ~BluetoothGattDescriptor();
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  nsCOMPtr<nsPIDOMWindow> mOwner;
+
+  /**
+   * Characteristic that this descriptor belongs to.
+   */
+  nsRefPtr<BluetoothGattCharacteristic> mCharacteristic;
+
+  /**
+   * GattId of this GATT descriptor which contains
+   * 1) mUuid: UUID of this descriptor in byte array format.
+   * 2) mInstanceId: Instance id of this descriptor.
+   */
+  BluetoothGattId mDescriptorId;
+
+  /**
+   * UUID string of this GATT descriptor.
+   */
+  nsString mUuidStr;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattService.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
+#include "mozilla/dom/BluetoothGattServiceBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
+#include "mozilla/dom/bluetooth/BluetoothGattService.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+USING_BLUETOOTH_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
+  BluetoothGattService, mOwner, mIncludedServices, mCharacteristics)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattService)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattService)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattService)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+BluetoothGattService::BluetoothGattService(
+  nsPIDOMWindow* aOwner, const nsAString& aAppUuid,
+  const BluetoothGattServiceId& aServiceId)
+  : mOwner(aOwner)
+  , mAppUuid(aAppUuid)
+  , mServiceId(aServiceId)
+{
+  MOZ_ASSERT(aOwner);
+  MOZ_ASSERT(!mAppUuid.IsEmpty());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  // Generate bluetooth signal path and a string representation to provide
+  // uuid of this service to applications
+  nsString path;
+  GeneratePathFromGattId(mServiceId.mId, path, mUuidStr);
+  bs->RegisterBluetoothSignalHandler(path, this);
+}
+
+BluetoothGattService::~BluetoothGattService()
+{
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  nsString path;
+  GeneratePathFromGattId(mServiceId.mId, path);
+  bs->UnregisterBluetoothSignalHandler(path, this);
+}
+
+void
+BluetoothGattService::HandleIncludedServicesDiscovered(
+  const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattServiceId);
+
+  const InfallibleTArray<BluetoothGattServiceId>& includedServIds =
+    aValue.get_ArrayOfBluetoothGattServiceId();
+
+  for (uint32_t i = 0; i < includedServIds.Length(); i++) {
+    mIncludedServices.AppendElement(new BluetoothGattService(
+      GetParentObject(), mAppUuid, includedServIds[i]));
+  }
+
+  BluetoothGattServiceBinding::ClearCachedIncludedServicesValue(this);
+}
+
+void
+BluetoothGattService::HandleCharacteristicsDiscovered(
+  const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattId);
+
+  const InfallibleTArray<BluetoothGattId>& characteristicIds =
+    aValue.get_ArrayOfBluetoothGattId();
+
+  for (uint32_t i = 0; i < characteristicIds.Length(); i++) {
+    mCharacteristics.AppendElement(new BluetoothGattCharacteristic(
+      GetParentObject(), this, characteristicIds[i]));
+  }
+
+  BluetoothGattServiceBinding::ClearCachedCharacteristicsValue(this);
+}
+
+void
+BluetoothGattService::Notify(const BluetoothSignal& aData)
+{
+  BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+
+  BluetoothValue v = aData.value();
+  if (aData.name().EqualsLiteral("IncludedServicesDiscovered")) {
+    HandleIncludedServicesDiscovered(v);
+  } else if (aData.name().EqualsLiteral("CharacteristicsDiscovered")) {
+    HandleCharacteristicsDiscovered(v);
+  } else {
+    BT_WARNING("Not handling GATT Service signal: %s",
+               NS_ConvertUTF16toUTF8(aData.name()).get());
+  }
+}
+
+JSObject*
+BluetoothGattService::WrapObject(JSContext* aContext,
+                                 JS::Handle<JSObject*> aGivenProto)
+{
+  return BluetoothGattServiceBinding::Wrap(aContext, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattService.h
@@ -0,0 +1,146 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothgattservice_h__
+#define mozilla_dom_bluetooth_bluetoothgattservice_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BluetoothGattServiceBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+#include "nsPIDOMWindow.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothSignal;
+class BluetoothValue;
+
+class BluetoothGattService final : public nsISupports
+                                 , public nsWrapperCache
+                                 , public BluetoothSignalObserver
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattService)
+
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
+  bool IsPrimary() const
+  {
+    return mServiceId.mIsPrimary;
+  }
+
+  void GetUuid(nsString& aUuidStr) const
+  {
+    aUuidStr = mUuidStr;
+  }
+
+  int InstanceId() const
+  {
+    return mServiceId.mId.mInstanceId;
+  }
+
+  void GetIncludedServices(
+    nsTArray<nsRefPtr<BluetoothGattService>>& aIncludedServices) const
+  {
+    aIncludedServices = mIncludedServices;
+  }
+
+  void GetCharacteristics(
+    nsTArray<nsRefPtr<BluetoothGattCharacteristic>>& aCharacteristics) const
+  {
+    aCharacteristics = mCharacteristics;
+  }
+
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  const nsAString& GetAppUuid() const
+  {
+    return mAppUuid;
+  }
+
+  const BluetoothGattServiceId& GetServiceId() const
+  {
+    return mServiceId;
+  }
+
+  void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+     return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  BluetoothGattService(nsPIDOMWindow* aOwner,
+                       const nsAString& aAppUuid,
+                       const BluetoothGattServiceId& aServiceId);
+
+private:
+  ~BluetoothGattService();
+
+  /**
+   * Add newly discovered GATT included services into mIncludedServices and
+   * update the cache value of mIncludedServices.
+   *
+   * @param aValue [in] BluetoothValue which contains an array of
+   *                    BluetoothGattServiceId of all discovered included
+   *                    services.
+   */
+  void HandleIncludedServicesDiscovered(const BluetoothValue& aValue);
+
+  /**
+   * Add newly discovered GATT characteristics into mCharacteristics and
+   * update the cache value of mCharacteristics.
+   *
+   * @param aValue [in] BluetoothValue which contains an array of
+   *                    BluetoothGattId of all discovered characteristics.
+   */
+  void HandleCharacteristicsDiscovered(const BluetoothValue& aValue);
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  nsCOMPtr<nsPIDOMWindow> mOwner;
+
+  /**
+   * UUID of the GATT client.
+   */
+  nsString mAppUuid;
+
+  /**
+   * ServiceId of this GATT service which contains
+   * 1) mId.mUuid: UUID of this service in byte array format.
+   * 2) mId.mInstanceId: Instance id of this service.
+   * 3) mIsPrimary: Indicate whether this is a primary service or not.
+   */
+  BluetoothGattServiceId mServiceId;
+
+  /**
+   * UUID string of this GATT service.
+   */
+  nsString mUuidStr;
+
+  /**
+   * Array of discovered included services for this service.
+   */
+  nsTArray<nsRefPtr<BluetoothGattService>> mIncludedServices;
+
+  /**
+   * Array of discovered characteristics for this service.
+   */
+  nsTArray<nsRefPtr<BluetoothGattCharacteristic>> mCharacteristics;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth2/moz.build
+++ b/dom/bluetooth2/moz.build
@@ -6,16 +6,19 @@
 
 if CONFIG['MOZ_B2G_BT']:
     SOURCES += [
         'BluetoothAdapter.cpp',
         'BluetoothClassOfDevice.cpp',
         'BluetoothDevice.cpp',
         'BluetoothDiscoveryHandle.cpp',
         'BluetoothGatt.cpp',
+        'BluetoothGattCharacteristic.cpp',
+        'BluetoothGattDescriptor.cpp',
+        'BluetoothGattService.cpp',
         'BluetoothHidManager.cpp',
         'BluetoothInterface.cpp',
         'BluetoothManager.cpp',
         'BluetoothPairingHandle.cpp',
         'BluetoothPairingListener.cpp',
         'BluetoothProfileController.cpp',
         'BluetoothReplyRunnable.cpp',
         'BluetoothService.cpp',
@@ -119,16 +122,19 @@ EXPORTS.mozilla.dom.bluetooth.ipc += [
 
 EXPORTS.mozilla.dom.bluetooth += [
     'BluetoothAdapter.h',
     'BluetoothClassOfDevice.h',
     'BluetoothCommon.h',
     'BluetoothDevice.h',
     'BluetoothDiscoveryHandle.h',
     'BluetoothGatt.h',
+    'BluetoothGattCharacteristic.h',
+    'BluetoothGattDescriptor.h',
+    'BluetoothGattService.h',
     'BluetoothManager.h',
     'BluetoothPairingHandle.h',
     'BluetoothPairingListener.h',
 ]
 
 IPDL_SOURCES += [
     'ipc/BluetoothTypes.ipdlh',
     'ipc/PBluetooth.ipdl',
--- a/dom/webidl/BluetoothGatt.webidl
+++ b/dom/webidl/BluetoothGatt.webidl
@@ -2,16 +2,18 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 [CheckPermissions="bluetooth"]
 interface BluetoothGatt : EventTarget
 {
+  [Cached, Pure]
+  readonly attribute sequence<BluetoothGattService> services;
   readonly attribute BluetoothConnectionState       connectionState;
 
   // Fired when attribute connectionState changed
            attribute EventHandler                   onconnectionstatechanged;
 
   /**
    * Connect/Disconnect to the remote BLE device if the connectionState is
    * disconnected/connected. Otherwise, the Promise will be rejected directly.
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothGattCharacteristic.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[CheckPermissions="bluetooth"]
+interface BluetoothGattCharacteristic
+{
+  readonly attribute BluetoothGattService                   service;
+  [Cached, Pure]
+  readonly attribute sequence<BluetoothGattDescriptor>      descriptors;
+
+  readonly attribute DOMString                              uuid;
+  readonly attribute unsigned short                         instanceId;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothGattDescriptor.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[CheckPermissions="bluetooth"]
+interface BluetoothGattDescriptor
+{
+  readonly attribute BluetoothGattCharacteristic            characteristic;
+  readonly attribute DOMString                              uuid;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothGattService.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[CheckPermissions="bluetooth"]
+interface BluetoothGattService
+{
+  [Cached, Pure]
+  readonly attribute sequence<BluetoothGattCharacteristic>  characteristics;
+  [Cached, Pure]
+  readonly attribute sequence<BluetoothGattService>         includedServices;
+
+  readonly attribute boolean                                isPrimary;
+  readonly attribute DOMString                              uuid;
+  readonly attribute unsigned short                         instanceId;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -632,16 +632,19 @@ if CONFIG['MOZ_DEBUG']:
 if CONFIG['MOZ_B2G_BT']:
     if CONFIG['MOZ_B2G_BT_API_V2']:
         WEBIDL_FILES += [
             'BluetoothAdapter2.webidl',
             'BluetoothClassOfDevice.webidl',
             'BluetoothDevice2.webidl',
             'BluetoothDiscoveryHandle.webidl',
             'BluetoothGatt.webidl',
+            'BluetoothGattCharacteristic.webidl',
+            'BluetoothGattDescriptor.webidl',
+            'BluetoothGattService.webidl',
             'BluetoothManager2.webidl',
             'BluetoothPairingHandle.webidl',
             'BluetoothPairingListener.webidl',
         ]
     else:
         WEBIDL_FILES += [
             'BluetoothAdapter.webidl',
             'BluetoothDevice.webidl',