Merge b2g-inbound to m-c a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Mon, 23 Mar 2015 16:47:58 -0700
changeset 263970 235a9cb26548a76b85a67af8845746ac27ca2e7a
parent 263940 e642ae3c0496b4a3b177d060fe0d43b85dd3fd1e (current diff)
parent 263969 e07424c98a7df294ce84a804af724393855f78a2 (diff)
child 263971 840cfd5bc9712a9dbccb829b71abc5fdcfd00020
child 264019 7f5abc27fd5366c596a98b6b06169542b89dccb9
child 264111 4f7a4da89137ad807cd0f0c1452059381dd0e6e4
child 264147 a8194e05e0477c62c5dd89cbf05c5665454b4a27
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
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
Merge b2g-inbound to m-c a=merge CLOSED TREE
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "8eac260ee81a8aca05770d18c5736536d44ee7a7", 
+        "git_revision": "efebbafd12fc42ddcd378948b683a51106517660", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "1400d176ecef76d06b012fb082c246eb17d1d30f", 
+    "revision": "d8e53e5d917b1ce79aea842e8340ce82799cac3e", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- 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/BluetoothCommon.h
+++ b/dom/bluetooth2/BluetoothCommon.h
@@ -280,16 +280,26 @@ enum BluetoothSspVariant {
   SSP_VARIANT_PASSKEY_CONFIRMATION,
   SSP_VARIANT_PASSKEY_ENTRY,
   SSP_VARIANT_CONSENT,
   SSP_VARIANT_PASSKEY_NOTIFICATION
 };
 
 struct BluetoothUuid {
   uint8_t mUuid[16];
+
+  bool operator==(const BluetoothUuid& aOther) const
+  {
+    for (uint8_t i = 0; i < sizeof(mUuid); i++) {
+      if (mUuid[i] != aOther.mUuid[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
 };
 
 struct BluetoothServiceRecord {
   BluetoothUuid mUuid;
   uint16_t mChannel;
   char mName[256];
 };
 
@@ -542,21 +552,31 @@ enum BluetoothGattStatus {
 
 struct BluetoothGattAdvData {
   uint8_t mAdvData[62];
 };
 
 struct BluetoothGattId {
   BluetoothUuid mUuid;
   uint8_t mInstanceId;
+
+  bool operator==(const BluetoothGattId& aOther) const
+  {
+    return mUuid == aOther.mUuid && mInstanceId == aOther.mInstanceId;
+  }
 };
 
 struct BluetoothGattServiceId {
   BluetoothGattId mId;
   uint8_t mIsPrimary;
+
+  bool operator==(const BluetoothGattServiceId& aOther) const
+  {
+    return mId == aOther.mId && mIsPrimary == aOther.mIsPrimary;
+  }
 };
 
 struct BluetoothGattReadParam {
   BluetoothGattServiceId mServiceId;
   BluetoothGattId mCharId;
   BluetoothGattId mDescriptorId;
   uint8_t mValue[BLUETOOTH_GATT_MAX_ATTR_LEN];
   uint16_t mValueLength;
--- a/dom/bluetooth2/BluetoothGatt.cpp
+++ b/dom/bluetooth2/BluetoothGatt.cpp
@@ -1,52 +1,48 @@
 /* -*- 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,
                              const nsAString& aDeviceAddr)
   : DOMEventTargetHelper(aWindow)
   , mAppUuid(EmptyString())
   , mClientIf(0)
   , mConnectionState(BluetoothConnectionState::Disconnected)
   , mDeviceAddr(aDeviceAddr)
+  , mDiscoveringServices(false)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(!mDeviceAddr.IsEmpty());
 }
 
 BluetoothGatt::~BluetoothGatt()
 {
   BluetoothService* bs = BluetoothService::Get();
@@ -211,16 +207,46 @@ BluetoothGatt::ReadRemoteRssi(ErrorResul
 
   nsRefPtr<BluetoothReplyRunnable> result =
     new ReadRemoteRssiTask(promise);
   bs->GattClientReadRemoteRssiInternal(mClientIf, mDeviceAddr, result);
 
   return promise.forget();
 }
 
+already_AddRefed<Promise>
+BluetoothGatt::DiscoverServices(ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+  if (!global) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
+
+  BT_ENSURE_TRUE_REJECT(
+    mConnectionState == BluetoothConnectionState::Connected &&
+    !mDiscoveringServices,
+    NS_ERROR_DOM_INVALID_STATE_ERR);
+
+  BluetoothService* bs = BluetoothService::Get();
+  BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
+
+  mDiscoveringServices = true;
+  nsRefPtr<BluetoothReplyRunnable> result =
+    new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
+                                   promise,
+                                   NS_LITERAL_STRING("DiscoverGattServices"));
+  bs->DiscoverGattServicesInternal(mAppUuid, result);
+
+  return promise.forget();
+}
+
 void
 BluetoothGatt::UpdateConnectionState(BluetoothConnectionState aState)
 {
   BT_API2_LOGR("GATT connection state changes to: %d", int(aState));
   mConnectionState = aState;
 
   // Dispatch connectionstatechanged event to application
   nsCOMPtr<nsIDOMEvent> event;
@@ -231,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();
@@ -248,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,26 +37,32 @@ 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)
    ***************************************************************************/
   already_AddRefed<Promise> Connect(ErrorResult& aRv);
   already_AddRefed<Promise> Disconnect(ErrorResult& aRv);
+  already_AddRefed<Promise> DiscoverServices(ErrorResult& aRv);
   already_AddRefed<Promise> ReadRemoteRssi(ErrorResult& aRv);
 
   /****************************************************************************
    * Others
    ***************************************************************************/
   void Notify(const BluetoothSignal& aParam); // BluetoothSignalObserver
 
   nsPIDOMWindow* GetParentObject() const
@@ -82,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;
 
@@ -105,13 +121,23 @@ private:
    * Connection state of this remote device.
    */
   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/BluetoothInterface.h
+++ b/dom/bluetooth2/BluetoothInterface.h
@@ -686,30 +686,34 @@ public:
 
   /* Clear the attribute cache for a given device*/
   virtual void Refresh(int aClientIf,
                        const nsAString& aBdAddr,
                        BluetoothGattClientResultHandler* aRes) = 0;
 
   /* Enumerate Attributes */
   virtual void SearchService(int aConnId,
+                             bool aSearchAll,
                              const BluetoothUuid& aUuid,
                              BluetoothGattClientResultHandler* aRes) = 0;
   virtual void GetIncludedService(
     int aConnId,
     const BluetoothGattServiceId& aServiceId,
+    bool aFirst,
     const BluetoothGattServiceId& aStartServiceId,
     BluetoothGattClientResultHandler* aRes) = 0;
   virtual void GetCharacteristic(int aConnId,
                                  const BluetoothGattServiceId& aServiceId,
+                                 bool aFirst,
                                  const BluetoothGattId& aStartCharId,
                                  BluetoothGattClientResultHandler* aRes) = 0;
   virtual void GetDescriptor(int aConnId,
                              const BluetoothGattServiceId& aServiceId,
                              const BluetoothGattId& aCharId,
+                             bool aFirst,
                              const BluetoothGattId& aDescriptorId,
                              BluetoothGattClientResultHandler* aRes) = 0;
 
   /* Read / Write An Attribute */
   virtual void ReadCharacteristic(int aConnId,
                                   const BluetoothGattServiceId& aServiceId,
                                   const BluetoothGattId& aCharId,
                                   int aAuthReq,
--- a/dom/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth2/BluetoothService.h
@@ -338,16 +338,24 @@ public:
    * (platform specific implementation)
    */
   virtual void
   DisconnectGattClientInternal(const nsAString& aAppUuid,
                                const nsAString& aDeviceAddress,
                                BluetoothReplyRunnable* aRunnable) = 0;
 
   /**
+   * Discover GATT services, characteristic, descriptors from a remote GATT
+   * server. (platform specific implementation)
+   */
+  virtual void
+  DiscoverGattServicesInternal(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable) = 0;
+
+  /**
    * Unregister a GATT client. (platform specific implementation)
    */
   virtual void
   UnregisterGattClientInternal(int aClientIf,
                                BluetoothReplyRunnable* aRunnable) = 0;
 
   /**
    * Request RSSI for a remote GATT server. (platform specific implementation)
--- a/dom/bluetooth2/BluetoothUtils.cpp
+++ b/dom/bluetooth2/BluetoothUtils.cpp
@@ -36,16 +36,27 @@ UuidToString(const BluetoothUuid& aUuid,
           ntohs(uuid2), ntohs(uuid3),
           ntohl(uuid4), ntohs(uuid5));
 
   aString.Truncate();
   aString.AssignLiteral(uuidStr);
 }
 
 void
+ReversedUuidToString(const BluetoothUuid& aUuid, nsAString& aString)
+{
+  BluetoothUuid uuid;
+  for (uint8_t i = 0; i < 16; i++) {
+    uuid.mUuid[i] = aUuid.mUuid[15 - i];
+  }
+
+  UuidToString(uuid, aString);
+}
+
+void
 StringToUuid(const char* aString, BluetoothUuid& aUuid)
 {
   uint32_t uuid0, uuid4;
   uint16_t uuid1, uuid2, uuid3, uuid5;
 
   sscanf(aString, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
          &uuid0, &uuid1, &uuid2, &uuid3, &uuid4, &uuid5);
 
@@ -59,16 +70,36 @@ StringToUuid(const char* aString, Blueto
   memcpy(&aUuid.mUuid[0], &uuid0, sizeof(uint32_t));
   memcpy(&aUuid.mUuid[4], &uuid1, sizeof(uint16_t));
   memcpy(&aUuid.mUuid[6], &uuid2, sizeof(uint16_t));
   memcpy(&aUuid.mUuid[8], &uuid3, sizeof(uint16_t));
   memcpy(&aUuid.mUuid[10], &uuid4, sizeof(uint32_t));
   memcpy(&aUuid.mUuid[14], &uuid5, sizeof(uint16_t));
 }
 
+void
+GeneratePathFromGattId(const BluetoothGattId& aId,
+                       nsAString& aPath,
+                       nsAString& aUuidStr)
+{
+  ReversedUuidToString(aId.mUuid, aUuidStr);
+
+  aPath.Assign(aUuidStr);
+  aPath.AppendLiteral("_");
+  aPath.AppendInt(aId.mInstanceId);
+}
+
+void
+GeneratePathFromGattId(const BluetoothGattId& aId,
+                       nsAString& aPath)
+{
+  nsString uuidStr;
+  GeneratePathFromGattId(aId, aPath, uuidStr);
+}
+
 /**
  * |SetJsObject| is an internal function used by |BroadcastSystemMessage| only
  */
 bool
 SetJsObject(JSContext* aContext,
             const BluetoothValue& aValue,
             JS::Handle<JSObject*> aObj)
 {
--- a/dom/bluetooth2/BluetoothUtils.h
+++ b/dom/bluetooth2/BluetoothUtils.h
@@ -17,31 +17,72 @@ class BluetoothReplyRunnable;
 class BluetoothValue;
 
 //
 // BluetoothUuid <-> uuid string conversion
 //
 
 /**
  * Convert BluetoothUuid object to xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string.
- * This utility function is used by gecko internal only to convert BluetoothUuid
- * created by bluetooth stack to uuid string representation.
+ *
+ * Note: This utility function is used by gecko internal only to convert
+ * BluetoothUuid created by bluetooth stack to uuid string representation.
  */
 void
 UuidToString(const BluetoothUuid& aUuid, nsAString& aString);
 
 /**
+ * Convert BluetoothUuid object in a reversed byte order to
+ * xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string.
+ * Bluedroid stack reports the BluetoothUuid in a reversed byte order for
+ * GATT service, characteristic, descriptor uuids.
+ *
+ * Note: This utility function is used by gecko internal only to convert
+ * BluetoothUuid in a reversed byte order created by bluetooth stack to uuid
+ * string representation.
+ */
+void
+ReversedUuidToString(const BluetoothUuid& aUuid, nsAString& aString);
+
+/**
  * Convert xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string to BluetoothUuid object.
- * This utility function is used by gecko internal only to convert uuid string
- * created by gecko back to BluetoothUuid representation.
+ *
+ * Note: This utility function is used by gecko internal only to convert uuid
+ * string created by gecko back to BluetoothUuid representation.
  */
 void
 StringToUuid(const char* aString, BluetoothUuid& aUuid);
 
 //
+// Generate bluetooth signal path from GattId
+//
+
+/**
+ * Generate bluetooth signal path and UUID string from a GattId.
+ *
+ * @param aId      [in] GattId value to convert.
+ * @param aPath    [out] Bluetooth signal path generated from aId.
+ * @param aUuidStr [out] UUID string generated from aId.
+ */
+void
+GeneratePathFromGattId(const BluetoothGattId& aId,
+                       nsAString& aPath,
+                       nsAString& aUuidStr);
+
+/**
+ * Generate bluetooth signal path from a GattId.
+ *
+ * @param aId   [in] GattId value to convert.
+ * @param aPath [out] Bluetooth signal path generated from aId.
+ */
+void
+GeneratePathFromGattId(const BluetoothGattId& aId,
+                       nsAString& aPath);
+
+//
 // Broadcast system message
 //
 
 bool
 BroadcastSystemMessage(const nsAString& aType,
                        const BluetoothValue& aData);
 
 bool
--- a/dom/bluetooth2/bluedroid/BluetoothGattHALInterface.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothGattHALInterface.cpp
@@ -616,24 +616,26 @@ BluetoothGattClientHALInterface::Refresh
     DispatchBluetoothGattClientHALResult(
       aRes, &BluetoothGattClientResultHandler::Refresh,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
 void
 BluetoothGattClientHALInterface::SearchService(
-  int aConnId, const BluetoothUuid& aUuid,
+  int aConnId, bool aSearchAll, const BluetoothUuid& aUuid,
   BluetoothGattClientResultHandler* aRes)
 {
   bt_status_t status;
 #if ANDROID_VERSION >= 19
   bt_uuid_t uuid;
 
-  if (NS_SUCCEEDED(Convert(aUuid, uuid))) {
+  if (aSearchAll) {
+    status = mInterface->search_service(aConnId, 0);
+  } else if (NS_SUCCEEDED(Convert(aUuid, uuid))) {
     status = mInterface->search_service(aConnId, &uuid);
   } else {
     status = BT_STATUS_PARM_INVALID;
   }
 #else
   status = BT_STATUS_UNSUPPORTED;
 #endif
 
@@ -642,26 +644,28 @@ BluetoothGattClientHALInterface::SearchS
       aRes, &BluetoothGattClientResultHandler::SearchService,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
 void
 BluetoothGattClientHALInterface::GetIncludedService(
   int aConnId, const BluetoothGattServiceId& aServiceId,
-  const BluetoothGattServiceId& aStartServiceId,
+  bool aFirst, const BluetoothGattServiceId& aStartServiceId,
   BluetoothGattClientResultHandler* aRes)
 {
   bt_status_t status;
 #if ANDROID_VERSION >= 19
   btgatt_srvc_id_t serviceId;
   btgatt_srvc_id_t startServiceId;
 
-  if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
-      NS_SUCCEEDED(Convert(aStartServiceId, startServiceId))) {
+  if (aFirst && NS_SUCCEEDED(Convert(aServiceId, serviceId))) {
+    status = mInterface->get_included_service(aConnId, &serviceId, 0);
+  } else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
+             NS_SUCCEEDED(Convert(aStartServiceId, startServiceId))) {
     status = mInterface->get_included_service(aConnId, &serviceId,
                                               &startServiceId);
   } else {
     status = BT_STATUS_PARM_INVALID;
   }
 #else
   status = BT_STATUS_UNSUPPORTED;
 #endif
@@ -671,26 +675,28 @@ BluetoothGattClientHALInterface::GetIncl
       aRes, &BluetoothGattClientResultHandler::GetIncludedService,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
 void
 BluetoothGattClientHALInterface::GetCharacteristic(
   int aConnId, const BluetoothGattServiceId& aServiceId,
-  const BluetoothGattId& aStartCharId,
+  bool aFirst, const BluetoothGattId& aStartCharId,
   BluetoothGattClientResultHandler* aRes)
 {
   bt_status_t status;
 #if ANDROID_VERSION >= 19
   btgatt_srvc_id_t serviceId;
   btgatt_gatt_id_t startCharId;
 
-  if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
-      NS_SUCCEEDED(Convert(aStartCharId, startCharId))) {
+  if (aFirst && NS_SUCCEEDED(Convert(aServiceId, serviceId))) {
+    status = mInterface->get_characteristic(aConnId, &serviceId, 0);
+  } else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
+             NS_SUCCEEDED(Convert(aStartCharId, startCharId))) {
     status = mInterface->get_characteristic(aConnId, &serviceId, &startCharId);
   } else {
     status = BT_STATUS_PARM_INVALID;
   }
 #else
   status = BT_STATUS_UNSUPPORTED;
 #endif
 
@@ -699,29 +705,32 @@ BluetoothGattClientHALInterface::GetChar
       aRes, &BluetoothGattClientResultHandler::GetCharacteristic,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
 void
 BluetoothGattClientHALInterface::GetDescriptor(
   int aConnId, const BluetoothGattServiceId& aServiceId,
-  const BluetoothGattId& aCharId,
-  const BluetoothGattId& aDescriptorId,
-  BluetoothGattClientResultHandler* aRes)
+  const BluetoothGattId& aCharId, bool aFirst,
+  const BluetoothGattId& aDescriptorId, BluetoothGattClientResultHandler* aRes)
 {
   bt_status_t status;
 #if ANDROID_VERSION >= 19
   btgatt_srvc_id_t serviceId;
   btgatt_gatt_id_t charId;
   btgatt_gatt_id_t descriptorId;
 
-  if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
-      NS_SUCCEEDED(Convert(aCharId, charId)) &&
-      NS_SUCCEEDED(Convert(aDescriptorId, descriptorId))) {
+  if (aFirst &&
+      NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
+      NS_SUCCEEDED(Convert(aCharId, charId))) {
+    status = mInterface->get_descriptor(aConnId, &serviceId, &charId, 0);
+  } else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
+             NS_SUCCEEDED(Convert(aCharId, charId)) &&
+             NS_SUCCEEDED(Convert(aDescriptorId, descriptorId))) {
     status = mInterface->get_descriptor(aConnId, &serviceId, &charId,
                                         &descriptorId);
   } else {
     status = BT_STATUS_PARM_INVALID;
   }
 #else
   status = BT_STATUS_UNSUPPORTED;
 #endif
--- a/dom/bluetooth2/bluedroid/BluetoothGattHALInterface.h
+++ b/dom/bluetooth2/bluedroid/BluetoothGattHALInterface.h
@@ -51,29 +51,33 @@ public:
 
   /* Clear the attribute cache for a given device*/
   void Refresh(int aClientIf,
                const nsAString& aBdAddr,
                BluetoothGattClientResultHandler* aRes);
 
   /* Enumerate Attributes */
   void SearchService(int aConnId,
+                     bool aSearchAll,
                      const BluetoothUuid& aUuid,
                      BluetoothGattClientResultHandler* aRes);
   void GetIncludedService(int aConnId,
                           const BluetoothGattServiceId& aServiceId,
+                          bool aFirst,
                           const BluetoothGattServiceId& aStartServiceId,
                           BluetoothGattClientResultHandler* aRes);
   void GetCharacteristic(int aConnId,
                          const BluetoothGattServiceId& aServiceId,
+                         bool aFirst,
                          const BluetoothGattId& aStartCharId,
                          BluetoothGattClientResultHandler* aRes);
   void GetDescriptor(int aConnId,
                      const BluetoothGattServiceId& aServiceId,
                      const BluetoothGattId& aCharId,
+                     bool aFirst,
                      const BluetoothGattId& aDescriptorId,
                      BluetoothGattClientResultHandler* aRes);
 
   /* Read / Write An Attribute */
   void ReadCharacteristic(int aConnId,
                           const BluetoothGattServiceId& aServiceId,
                           const BluetoothGattId& aCharId,
                           int aAuthReq,
--- a/dom/bluetooth2/bluedroid/BluetoothGattManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothGattManager.cpp
@@ -1,22 +1,22 @@
 /* -*- 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 "BluetoothGattManager.h"
 
-#include "BluetoothCommon.h"
 #include "BluetoothInterface.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothUtils.h"
 #include "MainThreadUtils.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 
 #define ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(runnable)                       \
   do {                                                                        \
     if (!sBluetoothGattInterface) {                                           \
@@ -32,47 +32,87 @@ USING_BLUETOOTH_NAMESPACE
 namespace {
   StaticRefPtr<BluetoothGattManager> sBluetoothGattManager;
   static BluetoothGattInterface* sBluetoothGattInterface;
   static BluetoothGattClientInterface* sBluetoothGattClientInterface;
 } // anonymous namespace
 
 bool BluetoothGattManager::mInShutdown = false;
 
-class BluetoothGattClient;
 static StaticAutoPtr<nsTArray<nsRefPtr<BluetoothGattClient> > > sClients;
 
-class BluetoothGattClient final : public nsISupports
+class mozilla::dom::bluetooth::BluetoothGattClient final : public nsISupports
 {
 public:
   NS_DECL_ISUPPORTS
 
   BluetoothGattClient(const nsAString& aAppUuid, const nsAString& aDeviceAddr)
   : mAppUuid(aAppUuid)
   , mDeviceAddr(aDeviceAddr)
   , mClientIf(0)
   , mConnId(0)
   { }
 
   ~BluetoothGattClient()
   {
     mConnectRunnable = nullptr;
     mDisconnectRunnable = nullptr;
+    mDiscoverRunnable = nullptr;
     mUnregisterClientRunnable = nullptr;
     mReadRemoteRssiRunnable = nullptr;
   }
 
+  void NotifyDiscoverCompleted(bool aSuccess)
+  {
+    MOZ_ASSERT(!mAppUuid.IsEmpty());
+    MOZ_ASSERT(mDiscoverRunnable);
+
+    BluetoothService* bs = BluetoothService::Get();
+    NS_ENSURE_TRUE_VOID(bs);
+
+    // Notify application to clear the cache values of
+    // service/characteristic/descriptor.
+    bs->DistributeSignal(NS_LITERAL_STRING("DiscoverCompleted"),
+                         mAppUuid,
+                         BluetoothValue(aSuccess));
+
+    // Resolve/Reject the Promise.
+    if (aSuccess) {
+      DispatchReplySuccess(mDiscoverRunnable);
+    } else {
+      DispatchReplyError(mDiscoverRunnable,
+                         NS_LITERAL_STRING("Discover failed"));
+    }
+
+    // Cleanup
+    mServices.Clear();
+    mIncludedServices.Clear();
+    mCharacteristics.Clear();
+    mDescriptors.Clear();
+    mDiscoverRunnable = nullptr;
+  }
+
   nsString mAppUuid;
   nsString mDeviceAddr;
   int mClientIf;
   int mConnId;
   nsRefPtr<BluetoothReplyRunnable> mConnectRunnable;
   nsRefPtr<BluetoothReplyRunnable> mDisconnectRunnable;
+  nsRefPtr<BluetoothReplyRunnable> mDiscoverRunnable;
+  nsRefPtr<BluetoothReplyRunnable> mReadRemoteRssiRunnable;
   nsRefPtr<BluetoothReplyRunnable> mUnregisterClientRunnable;
-  nsRefPtr<BluetoothReplyRunnable> mReadRemoteRssiRunnable;
+
+  /**
+   * These temporary arrays are used only during discover operations.
+   * All of them are empty if there are no ongoing discover operations.
+   */
+  nsTArray<BluetoothGattServiceId> mServices;
+  nsTArray<BluetoothGattServiceId> mIncludedServices;
+  nsTArray<BluetoothGattId> mCharacteristics;
+  nsTArray<BluetoothGattId> mDescriptors;
 };
 
 NS_IMPL_ISUPPORTS0(BluetoothGattClient)
 
 class UuidComparator
 {
 public:
   bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
@@ -87,16 +127,26 @@ class ClientIfComparator
 public:
   bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
               int aClientIf) const
   {
     return aClient->mClientIf == aClientIf;
   }
 };
 
+class ConnIdComparator
+{
+public:
+  bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
+              int aConnId) const
+  {
+    return aClient->mConnId == aConnId;
+  }
+};
+
 BluetoothGattManager*
 BluetoothGattManager::Get()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // If sBluetoothGattManager already exists, exit early
   if (sBluetoothGattManager) {
     return sBluetoothGattManager;
@@ -333,23 +383,17 @@ BluetoothGattManager::UnregisterClient(i
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    ClientIfComparator());
-
-  // Reject the unregister request if the client is not found
-  if (index == sClients->NoIndex) {
-    DispatchReplyError(aRunnable,
-                       NS_LITERAL_STRING("Unregister GATT client failed"));
-    return;
-  }
+  MOZ_ASSERT(index != sClients->NoIndex);
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mUnregisterClientRunnable = aRunnable;
 
   sBluetoothGattClientInterface->UnregisterClient(
     aClientIf,
     new UnregisterClientResultHandler(client));
 }
@@ -464,33 +508,88 @@ BluetoothGattManager::Disconnect(const n
                                  BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
 
   size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
-
-  // Reject the disconnect request if the client is not found
-  if (index == sClients->NoIndex) {
-    DispatchReplyError(aRunnable, NS_LITERAL_STRING("Disconnect failed"));
-    return;
-  }
+  MOZ_ASSERT(index != sClients->NoIndex);
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mDisconnectRunnable = aRunnable;
 
   sBluetoothGattClientInterface->Disconnect(
     client->mClientIf,
     aDeviceAddr,
     client->mConnId,
     new DisconnectResultHandler(client));
 }
 
+class BluetoothGattManager::DiscoverResultHandler final
+  : public BluetoothGattClientResultHandler
+{
+public:
+  DiscoverResultHandler(BluetoothGattClient* aClient)
+  : mClient(aClient)
+  {
+    MOZ_ASSERT(mClient);
+  }
+
+  void OnError(BluetoothStatus aStatus) override
+  {
+    BT_WARNING("BluetoothGattClientInterface::Discover failed: %d",
+               (int)aStatus);
+
+    mClient->NotifyDiscoverCompleted(false);
+  }
+
+private:
+  nsRefPtr<BluetoothGattClient> mClient;
+};
+
+void
+BluetoothGattManager::Discover(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aRunnable);
+
+  ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
+
+  size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mConnId > 0);
+  MOZ_ASSERT(!client->mDiscoverRunnable);
+
+  client->mDiscoverRunnable = aRunnable;
+
+  /**
+   * Discover all services/characteristics/descriptors offered by the remote
+   * GATT server.
+   *
+   * The discover procesure includes following steps.
+   * 1) Discover all services.
+   * 2) After all services are discovered, for each service S, we will do
+   *    following actions.
+   *    2-1) Discover all included services of service S.
+   *    2-2) Discover all characteristics of service S.
+   *    2-3) Discover all descriptors of those characteristics discovered in
+   *         2-2).
+   */
+  sBluetoothGattClientInterface->SearchService(
+    client->mConnId,
+    true, // search all services
+    BluetoothUuid(),
+    new DiscoverResultHandler(client));
+}
+
 class BluetoothGattManager::ReadRemoteRssiResultHandler final
   : public BluetoothGattClientResultHandler
 {
 public:
   ReadRemoteRssiResultHandler(BluetoothGattClient* aClient)
   : mClient(aClient)
   {
     MOZ_ASSERT(mClient);
@@ -522,23 +621,17 @@ BluetoothGattManager::ReadRemoteRssi(int
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    ClientIfComparator());
-
-  // Reject the read remote rssi request if the client is not found
-  if (index == sClients->NoIndex) {
-    DispatchReplyError(aRunnable,
-                       NS_LITERAL_STRING("Read remote RSSI failed"));
-    return;
-  }
+  MOZ_ASSERT(index != sClients->NoIndex);
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mReadRemoteRssiRunnable = aRunnable;
 
   sBluetoothGattClientInterface->ReadRemoteRssi(
     aClientIf, aDeviceAddr,
     new ReadRemoteRssiResultHandler(client));
 }
@@ -553,17 +646,17 @@ BluetoothGattManager::RegisterClientNoti
 {
   BT_API2_LOGR("Client Registered, clientIf = %d", aClientIf);
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString uuid;
   UuidToString(aAppUuid, uuid);
 
   size_t index = sClients->IndexOf(uuid, 0 /* Start */, UuidComparator());
-  NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
+  MOZ_ASSERT(index != sClients->NoIndex);
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   if (aStatus != GATT_STATUS_SUCCESS) {
     BT_API2_LOGR(
       "RegisterClient failed, clientIf = %d, status = %d, appUuid = %s",
@@ -616,17 +709,17 @@ BluetoothGattManager::ConnectNotificatio
   BT_API2_LOGR();
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    ClientIfComparator());
-  NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
+  MOZ_ASSERT(index != sClients->NoIndex);
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
 
   if (aStatus != GATT_STATUS_SUCCESS) {
     BT_API2_LOGR("Connect failed, clientIf = %d, connId = %d, status = %d",
                  aClientIf, aConnId, aStatus);
 
     // Notify BluetoothGatt that the client remains disconnected
     bs->DistributeSignal(
@@ -668,17 +761,17 @@ BluetoothGattManager::DisconnectNotifica
   BT_API2_LOGR();
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    ClientIfComparator());
-  NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
+  MOZ_ASSERT(index != sClients->NoIndex);
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
 
   if (aStatus != GATT_STATUS_SUCCESS) {
     // Notify BluetoothGatt that the client remains connected
     bs->DistributeSignal(
       NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
       client->mAppUuid,
       BluetoothValue(true)); // Connected
@@ -706,45 +799,205 @@ BluetoothGattManager::DisconnectNotifica
     DispatchReplySuccess(client->mDisconnectRunnable);
     client->mDisconnectRunnable = nullptr;
   }
 }
 
 void
 BluetoothGattManager::SearchCompleteNotification(int aConnId,
                                                  BluetoothGattStatus aStatus)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mDiscoverRunnable);
+
+  if (aStatus != GATT_STATUS_SUCCESS) {
+    client->NotifyDiscoverCompleted(false);
+    return;
+  }
+
+  // Notify BluetoothGatt to create all services
+  bs->DistributeSignal(NS_LITERAL_STRING("ServicesDiscovered"),
+                       client->mAppUuid,
+                       BluetoothValue(client->mServices));
+
+  // All services are discovered, continue to search included services of each
+  // service if existed, otherwise, notify application that discover completed
+  if (!client->mServices.IsEmpty()) {
+    sBluetoothGattClientInterface->GetIncludedService(
+      aConnId,
+      client->mServices[0], // start from first service
+      true, // first included service
+      BluetoothGattServiceId(),
+      new DiscoverResultHandler(client));
+  } else {
+    client->NotifyDiscoverCompleted(true);
+  }
+}
 
 void
 BluetoothGattManager::SearchResultNotification(
   int aConnId, const BluetoothGattServiceId& aServiceId)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  // Save to mServices for distributing to application and discovering
+  // included services, characteristics of this service later
+  sClients->ElementAt(index)->mServices.AppendElement(aServiceId);
+}
 
 void
 BluetoothGattManager::GetCharacteristicNotification(
   int aConnId, BluetoothGattStatus aStatus,
   const BluetoothGattServiceId& aServiceId,
   const BluetoothGattId& aCharId,
   int aCharProperty)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mDiscoverRunnable);
+
+  if (aStatus == GATT_STATUS_SUCCESS) {
+    // Save to mCharacteristics for distributing to applications and
+    // discovering descriptors of this characteristic later
+    client->mCharacteristics.AppendElement(aCharId);
+
+    // Get next characteristic of this service
+    sBluetoothGattClientInterface->GetCharacteristic(
+      aConnId,
+      aServiceId,
+      false,
+      aCharId,
+      new DiscoverResultHandler(client));
+  } else { // all characteristics of this service are discovered
+    // Notify BluetoothGattService to create characteristics then proceed
+    nsString path;
+    GeneratePathFromGattId(aServiceId.mId, path);
+
+    bs->DistributeSignal(NS_LITERAL_STRING("CharacteristicsDiscovered"),
+                         path,
+                         BluetoothValue(client->mCharacteristics));
+
+    ProceedDiscoverProcess(client, aServiceId);
+  }
+}
 
 void
 BluetoothGattManager::GetDescriptorNotification(
   int aConnId, BluetoothGattStatus aStatus,
   const BluetoothGattServiceId& aServiceId,
   const BluetoothGattId& aCharId,
   const BluetoothGattId& aDescriptorId)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mDiscoverRunnable);
+
+  if (aStatus == GATT_STATUS_SUCCESS) {
+    // Save to mDescriptors for distributing to applications
+    client->mDescriptors.AppendElement(aDescriptorId);
+
+    // Get next descriptor of this characteristic
+    sBluetoothGattClientInterface->GetDescriptor(
+      aConnId,
+      aServiceId,
+      aCharId,
+      false,
+      aDescriptorId,
+      new DiscoverResultHandler(client));
+  } else { // all descriptors of this characteristic are discovered
+    // Notify BluetoothGattCharacteristic to create descriptors then proceed
+    nsString path;
+    GeneratePathFromGattId(aCharId, path);
+
+    bs->DistributeSignal(NS_LITERAL_STRING("DescriptorsDiscovered"),
+                         path,
+                         BluetoothValue(client->mDescriptors));
+    client->mDescriptors.Clear();
+
+    ProceedDiscoverProcess(client, aServiceId);
+  }
+}
 
 void
 BluetoothGattManager::GetIncludedServiceNotification(
   int aConnId, BluetoothGattStatus aStatus,
   const BluetoothGattServiceId& aServiceId,
   const BluetoothGattServiceId& aIncludedServId)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mDiscoverRunnable);
+
+  if (aStatus == GATT_STATUS_SUCCESS) {
+    // Save to mIncludedServices for distributing to applications
+    client->mIncludedServices.AppendElement(aIncludedServId);
+
+    // Get next included service of this service
+    sBluetoothGattClientInterface->GetIncludedService(
+      aConnId,
+      aServiceId,
+      false,
+      aIncludedServId,
+      new DiscoverResultHandler(client));
+  } else { // all included services of this service are discovered
+    // Notify BluetoothGattService to create included services
+    nsString path;
+    GeneratePathFromGattId(aServiceId.mId, path);
+
+    bs->DistributeSignal(NS_LITERAL_STRING("IncludedServicesDiscovered"),
+                         path,
+                         BluetoothValue(client->mIncludedServices));
+    client->mIncludedServices.Clear();
+
+    // Start to discover characteristics of this service
+    sBluetoothGattClientInterface->GetCharacteristic(
+      aConnId,
+      aServiceId,
+      true, // first characteristic
+      BluetoothGattId(),
+      new DiscoverResultHandler(client));
+  }
+}
 
 void
 BluetoothGattManager::RegisterNotificationNotification(
   int aConnId, int aIsRegister, BluetoothGattStatus aStatus,
   const BluetoothGattServiceId& aServiceId,
   const BluetoothGattId& aCharId)
 { }
 
@@ -858,9 +1111,50 @@ BluetoothGattManager::Observe(nsISupport
 void
 BluetoothGattManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mInShutdown = true;
   sBluetoothGattManager = nullptr;
 }
 
+void
+BluetoothGattManager::ProceedDiscoverProcess(
+  BluetoothGattClient* aClient,
+  const BluetoothGattServiceId& aServiceId)
+{
+  /**
+   * This function will be called to decide how to proceed the discover process
+   * after discovering all characteristics of a given service, or after
+   * discovering all descriptors of a given characteristic.
+   *
+   * There are three cases here,
+   * 1) mCharacteristics is not empty:
+   *      Proceed to discover descriptors of the first saved characteristic.
+   * 2) mCharacteristics is empty but mServices is not empty:
+   *      This service does not have any saved characteristics left, proceed to
+   *      discover included services of the next service.
+   * 3) Both arrays are already empty:
+   *      Discover is done, notify application.
+   */
+  if (!aClient->mCharacteristics.IsEmpty()) {
+    sBluetoothGattClientInterface->GetDescriptor(
+      aClient->mConnId,
+      aServiceId,
+      aClient->mCharacteristics[0],
+      true, // first descriptor
+      BluetoothGattId(),
+      new DiscoverResultHandler(aClient));
+    aClient->mCharacteristics.RemoveElementAt(0);
+  } else if (!aClient->mServices.IsEmpty()) {
+    sBluetoothGattClientInterface->GetIncludedService(
+      aClient->mConnId,
+      aClient->mServices[0],
+      true, // first included service
+      BluetoothGattServiceId(),
+      new DiscoverResultHandler(aClient));
+    aClient->mServices.RemoveElementAt(0);
+  } else {
+    aClient->NotifyDiscoverCompleted(true);
+  }
+}
+
 NS_IMPL_ISUPPORTS(BluetoothGattManager, nsIObserver)
--- a/dom/bluetooth2/bluedroid/BluetoothGattManager.h
+++ b/dom/bluetooth2/bluedroid/BluetoothGattManager.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_bluetooth_bluetoothgattmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothInterface.h"
 #include "BluetoothProfileManagerBase.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
+class BluetoothGattClient;
 class BluetoothReplyRunnable;
 
 class BluetoothGattManager final : public nsIObserver
                                      , public BluetoothGattNotificationHandler
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -30,31 +31,35 @@ public:
   void Connect(const nsAString& aAppUuid,
                const nsAString& aDeviceAddr,
                BluetoothReplyRunnable* aRunnable);
 
   void Disconnect(const nsAString& aAppUuid,
                   const nsAString& aDeviceAddr,
                   BluetoothReplyRunnable* aRunnable);
 
+  void Discover(const nsAString& aAppUuid,
+                BluetoothReplyRunnable* aRunnable);
+
   void UnregisterClient(int aClientIf,
                         BluetoothReplyRunnable* aRunnable);
 
   void ReadRemoteRssi(int aClientIf,
                       const nsAString& aDeviceAddr,
                       BluetoothReplyRunnable* aRunnable);
 
 private:
   class CleanupResultHandler;
   class CleanupResultHandlerRunnable;
   class InitGattResultHandler;
   class RegisterClientResultHandler;
   class UnregisterClientResultHandler;
   class ConnectResultHandler;
   class DisconnectResultHandler;
+  class DiscoverResultHandler;
   class ReadRemoteRssiResultHandler;
 
   BluetoothGattManager();
 
   void HandleShutdown();
 
   void RegisterClientNotification(BluetoothGattStatus aStatus,
                                   int aClientIf,
@@ -132,14 +137,17 @@ private:
   void ReadRemoteRssiNotification(int aClientIf,
                                   const nsAString& aBdAddr,
                                   int aRssi,
                                   BluetoothGattStatus aStatus) override;
 
   void ListenNotification(BluetoothGattStatus aStatus,
                           int aServerIf) override;
 
+  void ProceedDiscoverProcess(BluetoothGattClient* aClient,
+                              const BluetoothGattServiceId& aServiceId);
+
   static bool mInShutdown;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
@@ -1113,16 +1113,30 @@ BluetoothServiceBluedroid::DisconnectGat
 
   BluetoothGattManager* gatt = BluetoothGattManager::Get();
   ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
 
   gatt->Disconnect(aAppUuid, aDeviceAddress, aRunnable);
 }
 
 void
+BluetoothServiceBluedroid::DiscoverGattServicesInternal(
+  const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
+
+  BluetoothGattManager* gatt = BluetoothGattManager::Get();
+  ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
+
+  gatt->Discover(aAppUuid, aRunnable);
+}
+
+void
 BluetoothServiceBluedroid::UnregisterGattClientInternal(
   int aClientIf, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
 
   BluetoothGattManager* gatt = BluetoothGattManager::Get();
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h
@@ -180,16 +180,20 @@ public:
                             BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   DisconnectGattClientInternal(const nsAString& aAppUuid,
                                const nsAString& aDeviceAddress,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
+  DiscoverGattServicesInternal(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable) override;
+
+  virtual void
   UnregisterGattClientInternal(int aClientIf,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   GattClientReadRemoteRssiInternal(
     int aClientIf, const nsAString& aDeviceAddress,
     BluetoothReplyRunnable* aRunnable) override;
 
--- a/dom/bluetooth2/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth2/bluez/BluetoothDBusService.cpp
@@ -4287,16 +4287,22 @@ BluetoothDBusService::ConnectGattClientI
 void
 BluetoothDBusService::DisconnectGattClientInternal(
   const nsAString& aAppUuid, const nsAString& aDeviceAddress,
   BluetoothReplyRunnable* aRunnable)
 {
 }
 
 void
+BluetoothDBusService::DiscoverGattServicesInternal(
+  const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
+{
+}
+
+void
 BluetoothDBusService::UnregisterGattClientInternal(
   int aClientIf, BluetoothReplyRunnable* aRunnable)
 {
 }
 
 void
 BluetoothDBusService::GattClientReadRemoteRssiInternal(
   int aClientIf, const nsAString& aDeviceAddress,
--- a/dom/bluetooth2/bluez/BluetoothDBusService.h
+++ b/dom/bluetooth2/bluez/BluetoothDBusService.h
@@ -189,16 +189,21 @@ public:
   ConnectGattClientInternal(const nsAString& aAppUuid,
                             const nsAString& aDeviceAddress,
                             BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   DisconnectGattClientInternal(const nsAString& aAppUuid,
                                const nsAString& aDeviceAddress,
                                BluetoothReplyRunnable* aRunnable) override;
+
+  virtual void
+  DiscoverGattServicesInternal(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable) override;
+
   virtual void
   UnregisterGattClientInternal(int aClientIf,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   GattClientReadRemoteRssiInternal(
     int aClientIf, const nsAString& aDeviceAddress,
     BluetoothReplyRunnable* aRunnable) override;
--- a/dom/bluetooth2/ipc/BluetoothMessageUtils.h
+++ b/dom/bluetooth2/ipc/BluetoothMessageUtils.h
@@ -23,11 +23,78 @@ struct ParamTraits<mozilla::dom::bluetoo
 template <>
 struct ParamTraits<mozilla::dom::bluetooth::BluetoothStatus>
   : public ContiguousEnumSerializer<
              mozilla::dom::bluetooth::BluetoothStatus,
              mozilla::dom::bluetooth::STATUS_SUCCESS,
              mozilla::dom::bluetooth::STATUS_RMT_DEV_DOWN>
 { };
 
+template <>
+struct ParamTraits<mozilla::dom::bluetooth::BluetoothUuid>
+{
+  typedef mozilla::dom::bluetooth::BluetoothUuid paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    for (uint8_t i = 0; i < 16; i++) {
+      WriteParam(aMsg, aParam.mUuid[i]);
+    }
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    for (uint8_t i = 0; i < 16; i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mUuid[i]))) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::dom::bluetooth::BluetoothGattId>
+{
+  typedef mozilla::dom::bluetooth::BluetoothGattId paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mUuid);
+    WriteParam(aMsg, aParam.mInstanceId);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mUuid)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mInstanceId))) {
+      return false;
+    }
+
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::dom::bluetooth::BluetoothGattServiceId>
+{
+  typedef mozilla::dom::bluetooth::BluetoothGattServiceId paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mId);
+    WriteParam(aMsg, aParam.mIsPrimary);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mId)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mIsPrimary))) {
+      return false;
+    }
+
+    return true;
+  }
+};
 } // namespace IPC
 
 #endif // mozilla_dom_bluetooth_ipc_bluetoothmessageutils_h__
--- a/dom/bluetooth2/ipc/BluetoothParent.cpp
+++ b/dom/bluetooth2/ipc/BluetoothParent.cpp
@@ -249,16 +249,18 @@ BluetoothParent::RecvPBluetoothRequestCo
     case Request::TSendMetaDataRequest:
       return actor->DoRequest(aRequest.get_SendMetaDataRequest());
     case Request::TSendPlayStatusRequest:
       return actor->DoRequest(aRequest.get_SendPlayStatusRequest());
     case Request::TConnectGattClientRequest:
       return actor->DoRequest(aRequest.get_ConnectGattClientRequest());
     case Request::TDisconnectGattClientRequest:
       return actor->DoRequest(aRequest.get_DisconnectGattClientRequest());
+    case Request::TDiscoverGattServicesRequest:
+      return actor->DoRequest(aRequest.get_DiscoverGattServicesRequest());
     case Request::TUnregisterGattClientRequest:
       return actor->DoRequest(aRequest.get_UnregisterGattClientRequest());
     case Request::TGattClientReadRemoteRssiRequest:
       return actor->DoRequest(aRequest.get_GattClientReadRemoteRssiRequest());
     default:
       MOZ_CRASH("Unknown type!");
   }
 
@@ -715,16 +717,28 @@ BluetoothRequestParent::DoRequest(const 
   mService->DisconnectGattClientInternal(aRequest.appUuid(),
                                          aRequest.deviceAddress(),
                                          mReplyRunnable.get());
 
   return true;
 }
 
 bool
+BluetoothRequestParent::DoRequest(const DiscoverGattServicesRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  MOZ_ASSERT(mRequestType == Request::TDiscoverGattServicesRequest);
+
+  mService->DiscoverGattServicesInternal(aRequest.appUuid(),
+                                         mReplyRunnable.get());
+
+  return true;
+}
+
+bool
 BluetoothRequestParent::DoRequest(const UnregisterGattClientRequest& aRequest)
 {
   MOZ_ASSERT(mService);
   MOZ_ASSERT(mRequestType == Request::TUnregisterGattClientRequest);
 
   mService->UnregisterGattClientInternal(aRequest.clientIf(),
                                          mReplyRunnable.get());
 
--- a/dom/bluetooth2/ipc/BluetoothParent.h
+++ b/dom/bluetooth2/ipc/BluetoothParent.h
@@ -219,16 +219,19 @@ protected:
 
   bool
   DoRequest(const ConnectGattClientRequest& aRequest);
 
   bool
   DoRequest(const DisconnectGattClientRequest& aRequest);
 
   bool
+  DoRequest(const DiscoverGattServicesRequest& aRequest);
+
+  bool
   DoRequest(const UnregisterGattClientRequest& aRequest);
 
   bool
   DoRequest(const GattClientReadRemoteRssiRequest& aRequest);
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp
@@ -393,16 +393,24 @@ BluetoothServiceChildProcess::Disconnect
   const nsAString& aAppUuid, const nsAString& aDeviceAddress,
   BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable,
     DisconnectGattClientRequest(nsString(aAppUuid), nsString(aDeviceAddress)));
 }
 
 void
+BluetoothServiceChildProcess::DiscoverGattServicesInternal(
+  const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
+{
+  SendRequest(aRunnable,
+    DiscoverGattServicesRequest(nsString(aAppUuid)));
+}
+
+void
 BluetoothServiceChildProcess::UnregisterGattClientInternal(
   int aClientIf, BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable, UnregisterGattClientRequest(aClientIf));
 }
 
 void
 BluetoothServiceChildProcess::GattClientReadRemoteRssiInternal(
--- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h
@@ -198,16 +198,20 @@ public:
                             BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   DisconnectGattClientInternal(const nsAString& aAppUuid,
                                const nsAString& aDeviceAddress,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
+  DiscoverGattServicesInternal(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable) override;
+
+  virtual void
   UnregisterGattClientInternal(int aClientIf,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   GattClientReadRemoteRssiInternal(int aClientIf,
                                    const nsAString& aDeviceAddress,
                                    BluetoothReplyRunnable* aRunnable) override;
 
--- a/dom/bluetooth2/ipc/BluetoothTypes.ipdlh
+++ b/dom/bluetooth2/ipc/BluetoothTypes.ipdlh
@@ -1,15 +1,20 @@
 /* -*- 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/. */
 
-using mozilla::dom::bluetooth::BluetoothStatus from "mozilla/dom/bluetooth/BluetoothCommon.h";
+using mozilla::dom::bluetooth::BluetoothGattId
+  from "mozilla/dom/bluetooth/BluetoothCommon.h";
+using mozilla::dom::bluetooth::BluetoothGattServiceId
+  from "mozilla/dom/bluetooth/BluetoothCommon.h";
+using mozilla::dom::bluetooth::BluetoothStatus
+  from "mozilla/dom/bluetooth/BluetoothCommon.h";
 
 namespace mozilla {
 namespace dom {
 namespace bluetooth {
 
 /**
  * Value structure for returns from bluetooth. Currently modeled after dbus
  * returns, which can be a 32-bit int, an UTF16 string, a bool, or an array of
@@ -18,16 +23,18 @@ namespace bluetooth {
 union BluetoothValue
 {
   uint32_t;
   nsString;
   bool;
   nsString[];
   uint8_t[];
   BluetoothNamedValue[];
+  BluetoothGattId[];
+  BluetoothGattServiceId[];
 };
 
 /**
  * Key-value pair for dicts returned by the bluetooth backend. Used for things
  * like property updates, where the property will have a name and a type.
  */
 struct BluetoothNamedValue
 {
--- a/dom/bluetooth2/ipc/PBluetooth.ipdl
+++ b/dom/bluetooth2/ipc/PBluetooth.ipdl
@@ -183,16 +183,21 @@ struct ConnectGattClientRequest
 };
 
 struct DisconnectGattClientRequest
 {
   nsString appUuid;
   nsString deviceAddress;
 };
 
+struct DiscoverGattServicesRequest
+{
+  nsString appUuid;
+};
+
 struct UnregisterGattClientRequest
 {
   int clientIf;
 };
 
 struct GattClientReadRemoteRssiRequest
 {
   int clientIf;
@@ -228,16 +233,17 @@ union Request
   IsScoConnectedRequest;
   AnswerWaitingCallRequest;
   IgnoreWaitingCallRequest;
   ToggleCallsRequest;
   SendMetaDataRequest;
   SendPlayStatusRequest;
   ConnectGattClientRequest;
   DisconnectGattClientRequest;
+  DiscoverGattServicesRequest;
   UnregisterGattClientRequest;
   GattClientReadRemoteRssiRequest;
 };
 
 protocol PBluetooth
 {
   manager PContent;
   manages PBluetoothRequest;
--- 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/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -519,17 +519,16 @@ skip-if = buildapp == 'mulet' || buildap
 [test_track.html]
 [test_track_disabled.html]
 [test_ul_attributes_reflection.html]
 [test_undoManager.html]
 [test_video_wakelock.html]
 skip-if = toolkit == 'android' || (toolkit == 'gonk' && debug) #bug 871015, bug 881443
 [test_input_files_not_nsIFile.html]
 [test_ignoreuserfocus.html]
-skip-if = (toolkit == 'gonk' && debug)
 [test_fragment_form_pointer.html]
 [test_bug1682.html]
 [test_bug1823.html]
 [test_bug57600.html]
 [test_bug196523.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage
 [test_bug199692.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' || e10s #bug 811644 #Bug 931116, b2g desktop specific, initial triage
--- 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.
@@ -24,16 +26,24 @@ interface BluetoothGatt : EventTarget
    *   3) Promise is resolved or rejected according to the operation result.
    */
   [NewObject]
   Promise<void>                                     connect();
   [NewObject]
   Promise<void>                                     disconnect();
 
   /**
+   * Discover services, characteristics, descriptors offered by the remote GATT
+   * server. The promise will be rejected if the connState is not connected or
+   * operation fails.
+   */
+  [NewObject]
+  Promise<void>                                     discoverServices();
+
+  /**
    * Read RSSI for the remote BLE device if the connectState is connected.
    * Otherwise, the Promise will be rejected directly.
    */
   [NewObject]
   Promise<short>                                    readRemoteRssi();
 };
 
 enum BluetoothConnectionState
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',
--- a/mozglue/build/Nuwa.cpp
+++ b/mozglue/build/Nuwa.cpp
@@ -101,22 +101,20 @@ static size_t getPageSize(void) {
   return PAGE_SIZE;
 #else
   #warning "Hard-coding page size to 4096 bytes"
   return 4096
 #endif
 }
 
 /**
- * The stack size is chosen carefully so the frozen threads doesn't consume too
- * much memory in the Nuwa process. The threads shouldn't run deep recursive
- * methods or do large allocations on the stack to avoid stack overflow.
+ * Use 1 MiB stack size as Android does.
  */
 #ifndef NUWA_STACK_SIZE
-#define NUWA_STACK_SIZE (1024 * 128)
+#define NUWA_STACK_SIZE (1024 * 1024)
 #endif
 
 #define NATIVE_THREAD_NAME_LENGTH 16
 
 typedef struct nuwa_construct {
   typedef void(*construct_t)(void*);
 
   construct_t construct;
@@ -168,16 +166,18 @@ struct thread_info : public mozilla::Lin
    * (if the pthread_mutex_trylock succeeded) or another thread may have already
    * held the lock.  If the recreated thread did lock the mutex we must balance
    * that with another unlock on the main thread, which is signaled by
    * condMutexNeedsBalancing.
    */
   pthread_mutex_t *condMutex;
   bool condMutexNeedsBalancing;
 
+  size_t stackSize;
+  size_t guardSize;
   void *stk;
 
   pid_t origNativeThreadID;
   pid_t recreatedNativeThreadID;
   char nativeThreadName[NATIVE_THREAD_NAME_LENGTH];
 };
 
 typedef struct thread_info thread_info_t;
@@ -541,53 +541,81 @@ thread_info_new(void) {
   /* link tinfo to sAllThreads */
   thread_info_t *tinfo = new thread_info_t();
   tinfo->flags = 0;
   tinfo->recrFunctions = nullptr;
   tinfo->recreatedThreadID = 0;
   tinfo->recreatedNativeThreadID = 0;
   tinfo->condMutex = nullptr;
   tinfo->condMutexNeedsBalancing = false;
-  tinfo->stk = MozTaggedAnonymousMmap(nullptr,
-                                      NUWA_STACK_SIZE + getPageSize(),
-                                      PROT_READ | PROT_WRITE,
-                                      MAP_PRIVATE | MAP_ANONYMOUS,
-                                      /* fd */ -1,
-                                      /* offset */ 0,
-                                      "nuwa-thread-stack");
 
-  // We use a smaller stack size. Add protection to stack overflow: mprotect()
-  // stack top (the page at the lowest address) so we crash instead of corrupt
-  // other content that is malloc()'d.
-  mprotect(tinfo->stk, getPageSize(), PROT_NONE);
-
-  pthread_attr_init(&tinfo->threadAttr);
+  // Default stack and guard size.
+  tinfo->stackSize = NUWA_STACK_SIZE;
+  tinfo->guardSize = getPageSize();
 
   REAL(pthread_mutex_lock)(&sThreadCountLock);
   // Insert to the tail.
   sAllThreads.insertBack(tinfo);
 
   sThreadCount++;
   pthread_cond_signal(&sThreadChangeCond);
   pthread_mutex_unlock(&sThreadCountLock);
 
   return tinfo;
 }
 
 static void
+thread_attr_init(thread_info_t *tinfo, const pthread_attr_t *tattr)
+{
+  pthread_attr_init(&tinfo->threadAttr);
+
+  if (tattr) {
+    // Override default thread stack and guard size with tattr.
+    pthread_attr_getstacksize(tattr, &tinfo->stackSize);
+    pthread_attr_getguardsize(tattr, &tinfo->guardSize);
+
+    size_t pageSize = getPageSize();
+
+    tinfo->stackSize = (tinfo->stackSize + pageSize - 1) % pageSize;
+    tinfo->guardSize = (tinfo->guardSize + pageSize - 1) % pageSize;
+
+    int detachState = 0;
+    pthread_attr_getdetachstate(tattr, &detachState);
+    pthread_attr_setdetachstate(&tinfo->threadAttr, detachState);
+  }
+
+  tinfo->stk = MozTaggedAnonymousMmap(nullptr,
+                                      tinfo->stackSize + tinfo->guardSize,
+                                      PROT_READ | PROT_WRITE,
+                                      MAP_PRIVATE | MAP_ANONYMOUS,
+                                      /* fd */ -1,
+                                      /* offset */ 0,
+                                      "nuwa-thread-stack");
+
+  // Add protection to stack overflow: mprotect() stack top (the page at the
+  // lowest address) so we crash instead of corrupt other content that is
+  // malloc()'d.
+  mprotect(tinfo->stk, tinfo->guardSize, PROT_NONE);
+
+  pthread_attr_setstack(&tinfo->threadAttr,
+                        (char*)tinfo->stk + tinfo->guardSize,
+                        tinfo->stackSize);
+}
+
+static void
 thread_info_cleanup(void *arg) {
   if (sNuwaForking) {
     // We shouldn't have any thread exiting when we are forking a new process.
     abort();
   }
 
   thread_info_t *tinfo = (thread_info_t *)arg;
   pthread_attr_destroy(&tinfo->threadAttr);
 
-  munmap(tinfo->stk, NUWA_STACK_SIZE + getPageSize());
+  munmap(tinfo->stk, tinfo->stackSize + tinfo->guardSize);
 
   REAL(pthread_mutex_lock)(&sThreadCountLock);
   /* unlink tinfo from sAllThreads */
   tinfo->remove();
   pthread_mutex_unlock(&sThreadCountLock);
 
   if (tinfo->recrFunctions) {
     delete tinfo->recrFunctions;
@@ -736,21 +764,19 @@ extern "C" MFBT_API int
                       const pthread_attr_t *attr,
                       void *(*start_routine) (void *),
                       void *arg) {
   if (!sIsNuwaProcess) {
     return REAL(pthread_create)(thread, attr, start_routine, arg);
   }
 
   thread_info_t *tinfo = thread_info_new();
+  thread_attr_init(tinfo, attr);
   tinfo->startupFunc = start_routine;
   tinfo->startupArg = arg;
-  pthread_attr_setstack(&tinfo->threadAttr,
-                        (char*)tinfo->stk + getPageSize(),
-                        NUWA_STACK_SIZE);
 
   int rv = REAL(pthread_create)(thread,
                                 &tinfo->threadAttr,
                                 thread_create_startup,
                                 tinfo);
   if (rv) {
     thread_info_cleanup(tinfo);
   } else {
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -149,16 +149,17 @@ SandboxFilterImplContent::Build() {
   /* ioctl() is for GL. Remove when GL proxy is implemented.
    * Additionally ioctl() might be a place where we want to have
    * argument filtering */
   Allow(SYSCALL(ioctl));
   Allow(SYSCALL(close));
   Allow(SYSCALL(munmap));
   Allow(SYSCALL(mprotect));
   Allow(SYSCALL(writev));
+  Allow(SYSCALL(pread64));
   AllowThreadClone();
   Allow(SYSCALL(brk));
 #if SYSCALL_EXISTS(set_thread_area)
   Allow(SYSCALL(set_thread_area));
 #endif
 
   Allow(SYSCALL(getpid));
   Allow(SYSCALL(gettid));
--- a/testing/taskcluster/taskcluster_graph/try_test_parser.py
+++ b/testing/taskcluster/taskcluster_graph/try_test_parser.py
@@ -12,16 +12,18 @@ def parse_test_opts(input_str):
     token = ''
     in_platforms = False
 
     def add_test(value):
         cur_test['test'] = value.strip()
         tests.insert(0, cur_test)
 
     def add_platform(value):
+        # Ensure platforms exists...
+        cur_test['platforms'] = cur_test.get('platforms', [])
         cur_test['platforms'].insert(0, value.strip())
 
     # This might be somewhat confusing but we parse the string _backwards_ so
     # there is no ambiguity over what state we are in.
     for char in reversed(input_str):
 
         # , indicates exiting a state
         if char == ',':
@@ -40,17 +42,16 @@ def parse_test_opts(input_str):
         elif char == '[':
             # Exiting platform state entering test state.
             add_platform(token)
             token = ''
             in_platforms = False
         elif char == ']':
             # Entering platform state.
             in_platforms = True
-            cur_test['platforms'] = []
         else:
             # Accumulator.
             token = char + token
 
     # Handle any left over tokens.
     if token:
         add_test(token)
 
--- a/testing/taskcluster/tests/test_try_test_parser.py
+++ b/testing/taskcluster/tests/test_try_test_parser.py
@@ -25,15 +25,27 @@ class TryTestParserTest(unittest.TestCas
                 { 'test': 'bar', 'platforms': ['b'] },
                 { 'test': 'baz' },
                 { 'test': 'qux', 'platforms': ['z'] },
                 { 'test': 'a' }
             ]
         )
 
         self.assertEquals(
+            parse_test_opts('mochitest-3[Ubuntu,10.6,10.8,Windows XP,Windows 7,Windows 8]'),
+            [
+                {
+                    'test': 'mochitest-3',
+                    'platforms': [
+                        'Ubuntu', '10.6', '10.8', 'Windows XP', 'Windows 7', 'Windows 8'
+                    ]
+                }
+            ]
+        )
+
+        self.assertEquals(
             parse_test_opts(''),
             []
         )
 
 if __name__ == '__main__':
     mozunit.main()